TensorFlow 2.4 : ガイド : 基本 – Ragged Tensor パート II (翻訳/解説)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 01/05/2021
* 本ページは、TensorFlow org サイトの Guide – TensorFlow Basics の以下のページの 後半部 を翻訳した上で
適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
- お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
- Windows PC のブラウザからご参加が可能です。スマートデバイスもご利用可能です。
人工知能研究開発支援 | 人工知能研修サービス | テレワーク & オンライン授業を支援 |
PoC(概念実証)を失敗させないための支援 (本支援はセミナーに参加しアンケートに回答した方を対象としています。 |
◆ お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。
株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション |
E-Mail:sales-info@classcat.com ; WebSite: https://www.classcat.com/ |
Facebook: https://www.facebook.com/ClassCatJP/ |
ガイド : 基本 – Ragged Tensor パート II
オーバーロードされた演算子
RaggedTensor クラスは標準的な Python 算術と比較演算子をオーバーロードし、基本的な要素単位の数学を遂行することを容易にします :
x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]]) y = tf.ragged.constant([[1, 1], [2], [3, 3, 3]]) print(x + y)
<tf.RaggedTensor [[2, 3], [5], [7, 8, 9]]>
オーバーロードされた演算子は要素単位の計算を遂行しますので、総ての二値演算への入力は同じ shape を持つか、同じ shape にブロードキャスト可能でなければなりません。最も単純なブロードキャスト可能なケースでは、単一のスカラーが ragged tensor の各値と要素単位で組み合わされます :
x = tf.ragged.constant([[1, 2], [3], [4, 5, 6]]) print(x + 3)
<tf.RaggedTensor [[4, 5], [6], [7, 8, 9]]>
より上級のケースの議論については、ブロードキャストのセクションを見てください。
ragged tensor は通常の Tennsor と同じ演算子のセットをオーバーロードします : 単項演算子 -, ~ と abs(); そして二項演算子 +, -, *, /, //, %, **, &, |, ^, ==, <, <=, >, and >=.
インデキシング
ragged tensor は多次元インデキシングとスライシングを含む、Python-スタイルのインデキシングをサポートします。以下のサンプルは 2-D と 3-D ragged tensor でインデックスする ragged tensor を実演します。
インデキシング・サンプル: 2D ragged tensor
queries = tf.ragged.constant( [['Who', 'is', 'George', 'Washington'], ['What', 'is', 'the', 'weather', 'tomorrow'], ['Goodnight']])
print(queries[1]) # A single query
tf.Tensor([b'What' b'is' b'the' b'weather' b'tomorrow'], shape=(5,), dtype=string)
print(queries[1, 2]) # A single word
tf.Tensor(b'the', shape=(), dtype=string)
print(queries[1:]) # Everything but the first row
<tf.RaggedTensor [[b'What', b'is', b'the', b'weather', b'tomorrow'], [b'Goodnight']]>
print(queries[:, :3]) # The first 3 words of each query
<tf.RaggedTensor [[b'Who', b'is', b'George'], [b'What', b'is', b'the'], [b'Goodnight']]>
print(queries[:, -2:]) # The last 2 words of each query
<tf.RaggedTensor [[b'George', b'Washington'], [b'weather', b'tomorrow'], [b'Goodnight']]>
インデキシング・サンプル: 3D ragged tensor
rt = tf.ragged.constant([[[1, 2, 3], [4]], [[5], [], [6]], [[7]], [[8, 9], [10]]])
print(rt[1]) # Second row (2-D RaggedTensor)
<tf.RaggedTensor [[5], [], [6]]>
print(rt[3, 0]) # First element of fourth row (1-D Tensor)
tf.Tensor([8 9], shape=(2,), dtype=int32)
print(rt[:, 1:3]) # Items 1-3 of each row (3-D RaggedTensor)
<tf.RaggedTensor [[[4]], [[], [6]], [], [[10]]]>
print(rt[:, -1:]) # Last item of each row (3-D RaggedTensor)
<tf.RaggedTensor [[[4]], [[6]], [[7]], [[10]]]>
RaggedTensor は多次元インデキシングとスライシングをサポートします、一つの制限とともに : ragged 次元へのインデキシングは許容されません。このケースは問題を含みます、何故ならば指し示された値は幾つかの行では存在するかもしれませんが、他ではそうではありません。そのような場合、私達は (1) IndexError を上げる ; (2) デフォルト値を使用する ; あるいは (3) その値はスキップして開始したものより少ない行を持つ tensor を返すべきか明白ではありません。guiding principles of Python (“In the face of ambiguity, refuse the temptation to guess” ) に従い、この演算を現在は許容していません。
Tensor タイプ変換
RaggedTensor クラスは RaggedTensor と tf.Tensor あるいは tf.SparseTensor の間の変換に利用できるメソッドを定義しています :
ragged_sentences = tf.ragged.constant([ ['Hi'], ['Welcome', 'to', 'the', 'fair'], ['Have', 'fun']])
# RaggedTensor -> Tensor print(ragged_sentences.to_tensor(default_value='', shape=[None, 10]))
tf.Tensor( [[b'Hi' b'' b'' b'' b'' b'' b'' b'' b'' b''] [b'Welcome' b'to' b'the' b'fair' b'' b'' b'' b'' b'' b''] [b'Have' b'fun' b'' b'' b'' b'' b'' b'' b'' b'']], shape=(3, 10), dtype=string)
# Tensor -> RaggedTensor x = [[1, 3, -1, -1], [2, -1, -1, -1], [4, 5, 8, 9]] print(tf.RaggedTensor.from_tensor(x, padding=-1))
<tf.RaggedTensor [[1, 3], [2], [4, 5, 8, 9]]>
#RaggedTensor -> SparseTensor print(ragged_sentences.to_sparse())
SparseTensor(indices=tf.Tensor( [[0 0] [1 0] [1 1] [1 2] [1 3] [2 0] [2 1]], shape=(7, 2), dtype=int64), values=tf.Tensor([b'Hi' b'Welcome' b'to' b'the' b'fair' b'Have' b'fun'], shape=(7,), dtype=string), dense_shape=tf.Tensor([3 4], shape=(2,), dtype=int64))
# SparseTensor -> RaggedTensor st = tf.SparseTensor(indices=[[0, 0], [2, 0], [2, 1]], values=['a', 'b', 'c'], dense_shape=[3, 3]) print(tf.RaggedTensor.from_sparse(st))
<tf.RaggedTensor [[b'a'], [], [b'b', b'c']]>
ragged tensor を評価する
ragged tensor 内の値にアクセスするには、以下が可能です :
- ragged tensor をネストされた python リストに変換するために tf.RaggedTensor.to_list() を使用する。
- ragged tensor を numpy 配列に変換するために tf.RaggedTensor.numpy() を使用します、その値はネストされた numpy 配列です。
- tf.RaggedTensor.values と tf.RaggedTensor.row_splits プロパティを使用するか、tf.RaggedTensor.row_lengths() と tf.RaggedTensor.value_rowids() のような行分割メソッドを使用して、ragged tensor をその成分に分解します。
- ragged tensor から値を選択するために Python インデキシングを使用します。
rt = tf.ragged.constant([[1, 2], [3, 4, 5], [6], [], [7]]) print("python list:", rt.to_list()) print("numpy array:", rt.numpy()) print("values:", rt.values.numpy()) print("splits:", rt.row_splits.numpy()) print("indexed value:", rt[1].numpy())
python list: [[1, 2], [3, 4, 5], [6], [], [7]] numpy array: [array([1, 2], dtype=int32) array([3, 4, 5], dtype=int32) array([6], dtype=int32) array([], dtype=int32) array([7], dtype=int32)] values: [1 2 3 4 5 6 7] splits: [0 2 5 6 6 7] indexed value: [3 4 5]
ブロードキャスト
ブロードキャストは異なる shape を持つ tensor を要素単位の演算のために互換な shape を持つようにするプロセスです。ブロードキャストのより多くの背景については、以下を見てください :
2 つの入力 x と y が互換な shape を持つようにブロードキャストするための基本ステップは :
- x と y が同じ数の次元を持たないのであれば、それらがそうなるまで (サイズ 1 で) 外側の次元を追加します。
- x と y が異なるサイズを持つような各次元について :
- x か y が次元 d でサイズ 1 を持つ場合、他の入力のサイズに一致するように次元 d に渡りその値を繰り返します。
- そうでないなら、例外を上げます (x と y はブロードキャスト互換ではありません)。
ここで一様な次元の tensor のサイズは単一数です (その次元に渡るスライスのサイズ) ; そして ragged 次元の tensor のサイズは (その次元に渡る総てのスライスのための) スライス長のリストです。
ブロードキャスト・サンプル
# x (2D ragged): 2 x (num_rows) # y (scalar) # result (2D ragged): 2 x (num_rows) x = tf.ragged.constant([[1, 2], [3]]) y = 3 print(x + y)
<tf.RaggedTensor [[4, 5], [6]]>
# x (2d ragged): 3 x (num_rows) # y (2d tensor): 3 x 1 # Result (2d ragged): 3 x (num_rows) x = tf.ragged.constant( [[10, 87, 12], [19, 53], [12, 32]]) y = [[1000], [2000], [3000]] print(x + y)
<tf.RaggedTensor [[1010, 1087, 1012], [2019, 2053], [3012, 3032]]>
# x (3d ragged): 2 x (r1) x 2 # y (2d ragged): 1 x 1 # Result (3d ragged): 2 x (r1) x 2 x = tf.ragged.constant( [[[1, 2], [3, 4], [5, 6]], [[7, 8]]], ragged_rank=1) y = tf.constant([[10]]) print(x + y)
<tf.RaggedTensor [[[11, 12], [13, 14], [15, 16]], [[17, 18]]]>
# x (3d ragged): 2 x (r1) x (r2) x 1 # y (1d tensor): 3 # Result (3d ragged): 2 x (r1) x (r2) x 3 x = tf.ragged.constant( [ [ [[1], [2]], [], [[3]], [[4]], ], [ [[5], [6]], [[7]] ] ], ragged_rank=2) y = tf.constant([10, 20, 30]) print(x + y)
<tf.RaggedTensor [[[[11, 21, 31], [12, 22, 32]], [], [[13, 23, 33]], [[14, 24, 34]]], [[[15, 25, 35], [16, 26, 36]], [[17, 27, 37]]]]>
ここにブロードキャストしない shape の幾つかのサンプルがあります :
# x (2d ragged): 3 x (r1) # y (2d tensor): 3 x 4 # trailing dimensions do not match x = tf.ragged.constant([[1, 2], [3, 4, 5, 6], [7]]) y = tf.constant([[1, 2, 3, 4], [5, 6, 7, 8], [9, 10, 11, 12]]) try: x + y except tf.errors.InvalidArgumentError as exception: print(exception)
Expected 'tf.Tensor(False, shape=(), dtype=bool)' to be true. Summarized data: b'Unable to broadcast: dimension size mismatch in dimension' 1 b'lengths=' 4 b'dim_size=' 2, 4, 1
# x (2d ragged): 3 x (r1) # y (2d ragged): 3 x (r2) # ragged dimensions do not match. x = tf.ragged.constant([[1, 2, 3], [4], [5, 6]]) y = tf.ragged.constant([[10, 20], [30, 40], [50]]) try: x + y except tf.errors.InvalidArgumentError as exception: print(exception)
Expected 'tf.Tensor(False, shape=(), dtype=bool)' to be true. Summarized data: b'Unable to broadcast: dimension size mismatch in dimension' 1 b'lengths=' 2, 2, 1 b'dim_size=' 3, 1, 2
# x (3d ragged): 3 x (r1) x 2 # y (3d ragged): 3 x (r1) x 3 # trailing dimensions do not match x = tf.ragged.constant([[[1, 2], [3, 4], [5, 6]], [[7, 8], [9, 10]]]) y = tf.ragged.constant([[[1, 2, 0], [3, 4, 0], [5, 6, 0]], [[7, 8, 0], [9, 10, 0]]]) try: x + y except tf.errors.InvalidArgumentError as exception: print(exception)
Expected 'tf.Tensor(False, shape=(), dtype=bool)' to be true. Summarized data: b'Unable to broadcast: dimension size mismatch in dimension' 2 b'lengths=' 3, 3, 3, 3, 3 b'dim_size=' 2, 2, 2, 2, 2
RaggedTensor エンコーディング
ragged tensor は RaggedTensor クラスを使用してエンコードされます。内部的には各 RaggedTensor は以下から構成されます :
- 値 tensor、これは可変長行を平坦なリストに連結します。
- row_partition、これはこれらの平坦な値がどのように行に分割されるかを示します。
row_partition は異なるエンコーディングを使用してストアできます :
- row_splits は行の間の分割点を指定する整数ベクトルです。
- value_rowids は各値のための行インデックスを指定する整数ベクトルです。
- row_lengths は各行の長さを指定する整数ベクトルです。
- uniform_row_length は総ての行のための単一の長さを指定する整数スカラーです。
整数スカラー nrows もまた、value_rowids を持つ空の trailing 行、あるいは uniform_row_length を持つ空行を説明するために、 row_partition エンコーディングに含まれることができます。
rt = tf.RaggedTensor.from_row_splits( values=[3, 1, 4, 1, 5, 9, 2], row_splits=[0, 4, 4, 6, 7]) print(rt)
<tf.RaggedTensor [[3, 1, 4, 1], [], [5, 9], [2]]>
行分割のためにどのエンコーディングを使用するかの選択は、幾つかのコンテキストで効率性を改善するために内部的には ragged tensor で管理されます。特に、様々な行分割スキームの優位点と劣位点の幾つかは :
- 効率的なインデキシング: row_splits エンコーディングは ragged tensor への一定時間のインデキシングとスライシングを可能にします。
- 効率的な連結: row_lengths エンコーディングは ragged tensor を連結する時により効率的です、何故ならば 2 つの tensor が一緒に連結されるとき行の長さは変化しないからです。
- 小さいエンコーディング・サイズ: value_rowids エンコーディングは大きな数の空行を持つ ragged tensor をストアする時により効率的です、何故ならば tensor のサイズは値の総数にのみ依拠するからです。その一方で、row_splits と row_lengths エンコーディングはより長い行を持つ ragged tensor をストアする時により効率的です、何故ならばそれらは各行のために 1 つのスカラー値だけを必要とするからです。
- 互換性: value_rowids スキームは tf.segment_sum のような演算で使用される segmentation 形式に一致します。row_limits スキームは tf.sequence_mask のような ops で使用される形式に一致します。
- 一様な次元: 下で議論されるように、uniform_row_length エンコーディングは一様な次元を持つ ragged tensor をエンコードするために使用されます。
マルチ ragged 次元
マルチ ragged 次元を持つ ragged tensor は値 tensor のためのネストされた RaggedTensor を使用することによりエンコードされます。各ネストされた RaggedTensor は単一の ragged 次元を追加します。
rt = tf.RaggedTensor.from_row_splits( values=tf.RaggedTensor.from_row_splits( values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], row_splits=[0, 3, 3, 5, 9, 10]), row_splits=[0, 1, 1, 5]) print(rt) print("Shape: {}".format(rt.shape)) print("Number of partitioned dimensions: {}".format(rt.ragged_rank))
<tf.RaggedTensor [[[10, 11, 12]], [], [[], [13, 14], [15, 16, 17, 18], [19]]]> Shape: (3, None, None) Number of partitioned dimensions: 2
factory 関数 tf.RaggedTensor.from_nested_row_splits は、row_splits tensor のリストを提供することによりマルチ ragged 次元を持つ RaggedTensor を直接構築するために使用されるかもしれません :
rt = tf.RaggedTensor.from_nested_row_splits( flat_values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], nested_row_splits=([0, 1, 1, 5], [0, 3, 3, 5, 9, 10])) print(rt)
<tf.RaggedTensor [[[10, 11, 12]], [], [[], [13, 14], [15, 16, 17, 18], [19]]]>
ragged ランクと flat 値
ragged tensor の ragged ランクは基礎的な値 Tensor が分割された回数です (i.e. RaggedTensor オブジェクトのネストしている depth)。最も内側の値 tensor はその flat_values として知られています。次のサンプルでは、conversations は ragged_rank=3 を持ち、そしてその flat_values は 24 文字列を持つ 1D Tensor です :
# shape = [batch, (paragraph), (sentence), (word)] conversations = tf.ragged.constant( [[[["I", "like", "ragged", "tensors."]], [["Oh", "yeah?"], ["What", "can", "you", "use", "them", "for?"]], [["Processing", "variable", "length", "data!"]]], [[["I", "like", "cheese."], ["Do", "you?"]], [["Yes."], ["I", "do."]]]]) conversations.shape
TensorShape([2, None, None, None])
assert conversations.ragged_rank == len(conversations.nested_row_splits) conversations.ragged_rank # Number of partitioned dimensions.
3
conversations.flat_values.numpy()
array([b'I', b'like', b'ragged', b'tensors.', b'Oh', b'yeah?', b'What', b'can', b'you', b'use', b'them', b'for?', b'Processing', b'variable', b'length', b'data!', b'I', b'like', b'cheese.', b'Do', b'you?', b'Yes.', b'I', b'do.'], dtype=object)
一様な内側の (= inner) 次元
一様な内側の次元を持つ ragged tensor は flat_values (i.e. 最も内側の値) のために多次元 tf.Tensor を使用してエンコードされます。
rt = tf.RaggedTensor.from_row_splits( values=[[1, 3], [0, 0], [1, 3], [5, 3], [3, 3], [1, 2]], row_splits=[0, 3, 4, 6]) print(rt) print("Shape: {}".format(rt.shape)) print("Number of partitioned dimensions: {}".format(rt.ragged_rank)) print("Flat values shape: {}".format(rt.flat_values.shape)) print("Flat values:\n{}".format(rt.flat_values))
<tf.RaggedTensor [[[1, 3], [0, 0], [1, 3]], [[5, 3]], [[3, 3], [1, 2]]]> Shape: (3, None, 2) Number of partitioned dimensions: 1 Flat values shape: (6, 2) Flat values: [[1 3] [0 0] [1 3] [5 3] [3 3] [1 2]]
一様な非-内側の (= non-inner) 次元
一様な非-内側の次元を持つ ragged tensor は行を uniform_row_length で分割することによりエンコードされます。
rt = tf.RaggedTensor.from_uniform_row_length( values=tf.RaggedTensor.from_row_splits( values=[10, 11, 12, 13, 14, 15, 16, 17, 18, 19], row_splits=[0, 3, 5, 9, 10]), uniform_row_length=2) print(rt) print("Shape: {}".format(rt.shape)) print("Number of partitioned dimensions: {}".format(rt.ragged_rank))
<tf.RaggedTensor [[[10, 11, 12], [13, 14]], [[15, 16, 17, 18], [19]]]> Shape: (2, 2, None) Number of partitioned dimensions: 2
以上