TensorFlow Fold Blocks チュートリアル (Google翻訳バージョン)

TensorFlow FoldのBlocks Tutorial を翻訳しました。
嘘です。全部、Google翻訳です。

Blocks Tutorial
  • Introduction 
    • Motivating example
  • Basic concepts
  • Primitive blocks 
    • Converting Python objects into tensors.
    • Using TensorFlow tensors.
    • Functions and Layers
    • Python operations
  • Block composition
    • Wiring blocks together
    • Dealing with sequences
    • Dealing with records
    • Wiring things together, in more complicated ways.
    • Recursion and forward declaration

Introduction

読者がTensorFlowの基本的な概念と深い理解を理解していると仮定します。そうでない場合は、TensorFlowチュートリアルを開始するのがよいでしょう。

Foldモデルへの入力は、Pythonオブジェクトのミニバッチです。これらのオブジェクトは、プロトコルバッファ、JSON、XML、またはある種のカスタムパーサをデシリアライズして生成することができます。入力オブジェクトは木構造であると仮定される。 Foldモデルの出力は、TensorFlowテンソルの集合であり、通常の方法で損失関数とオプティマイザに接続できます。

入力としてデータ構造のミニバッチが与えられた場合、Foldは入力データを走査し、TensorFlowによって効率的に実行できる方法で操作を結合およびスケジューリングします。たとえば、ツリー内の各ノードが、共有ウェイトを持つ完全に接続されたレイヤーを使用してベクトルを出力する場合、Foldはツリーを単にトラバースするだけでなく、ベクトル行列の乗算演算を行います。代わりに、ツリー内の同じ深さのノードをマージします。これは、より大きい、より効率的な行列 - 行列乗算演算に並行して実行され、その後、出力行列を再びベクトルに分割します。

Motivating Example

次のコード例では、階層型のLSTMを実装しています。これはFoldでは簡単ですが、TensorFlowでは実行しにくいものです。

# Create RNN cells using the TensorFlow RNN library
char_cell = td.ScopedLayer(tf.contrib.rnn.BasicLSTMCell(num_units=16), 'char_cell')
word_cell = td.ScopedLayer(tf.contrib.rnn.BasicLSTMCell(num_units=32), 'word_cell')

# character LSTM converts a string to a word vector
char_lstm = (td.InputTransform(lambda s: [ord(c) for c in s]) >>
             td.Map(td.Scalar('int32') >>
                    td.Function(td.Embedding(128, 8))) >>
             td.RNN(char_cell))
# word LSTM converts a sequence of word vectors to a sentence vector.
word_lstm = td.Map(char_lstm >> td.GetItem(1)) >> td.RNN(word_cell)


階層的LSTMは文字列のリストを入力として受け取り、各文字列は単語であり、出力として文脈ベクトルを生成する。 2つの入れ子になったLSTMを使用してこれを行います。文字LSTMは文字列を入力とし、出力として単語ベクトルを生成します。文字列を整数のリスト(td.InputTransform)に変換し、埋め込みテーブル(td.Embedding)の各整数を検索し、埋め込みシーケンスをLSTMで処理して単語ベクトルを生成します。単語LSTMは単語LSTMを単語のシーケンスにマッピングして(td.Map)単語ベクトルのシーケンスを取得し、単語ベクトルを第2の大きなLSTMで処理して文章ベクトルを生成します。


以降のセクションでは、これらの操作について詳しく説明します。

Basic concepts

Foldモデルの基本コンポーネントはtd.Blockです。ブロックは本質的に関数であり、オブジェクトを入力として受け取り、出力として別のオブジェクトを生成します。問題のオブジェクトはテンソルであってもよいが、タプル、リスト、Python辞書、またはそれらの組み合わせであってもよい。 typesページでは、Foldタイプのシステムについて詳しく説明しています。

ブロックは、プログラミング言語の表現のように、ツリーに階層的に編成されています。ここでは、大きくて複雑なブロックが小さくて単純なブロックで構成されています。ブロック構造はDAGではなくツリーでなければならないことに注意してください。言い換えれば、各ブロック(すなわち、以下のブロッククラスの1つの各インスタンス)は、ツリー内で固有の位置を持たなければならない。型チェックとコンパイルの手順は、ツリーのプロパティに依存します。

Primitive blocks

プリミティブブロックは、ブロック階層の葉を形成し、テンソルについての基本的な計算を担う。

Pythonオブジェクトをテンソルに変換する。

td.Scalar()ブロックは、Pythonスカラーを0ランクのテンソルに変換します。
td.Vector(shape)ブロックは、Pythonリストを与えられた形状のテンソルに変換します。

TensorFlowテンソルを使用する。

td.FromTensorブロックは、TensorFlowテンソルをブロックにラップします。引数のない関数と同様に、FromTensorブロックは入力を受け付けません。出力として対応するテンソルを生成するだけです。 FromTensorはまた、numpyのテンソルで使用することもできます。

  td.FromTensor(np.zeros([])) # a constant with dtype=float64
  td.FromTensor(tf.zeros([]))  # a constant with dtype=float32
  td.FromTensor(tf.Variable(tf.zeros([])))  # a trainable variable

Functions and Layers

td.Functionブロックは、TensorFlow操作をブロックにラップします。テンソルを入力として受け取り、出力としてテンソルを生成します。複数の引数を取る、または複数の結果を生成する関数は、入出力としてテンソルのタプルを渡します。たとえば、td.Function(tf.add)は、2つのテンソルのタプルを入力とし、1つのテンソル(合計)を出力として生成するブロックです。

ファンクションブロックは、完全に接続されたレイヤーや埋め込みなど、一般的なニューラルネットワークの計算を実行するために、レイヤーと組み合わせて使用​​できます。 td.Layerは呼び出し可能なPythonオブジェクトで、レイヤーの異なるインスタンス間でウェイトを共有します。

以下の例では、ffnetは3層フィードフォワードネットワークで、3つのレイヤーのそれぞれが重みを共有しています。

# fclayer defines the weights for a fully-connected layer with 1024 hidden units.
fclayer = td.FC(1024)
# Each call to Function(fclayer) creates a fully-connected layer,
# all of which share the weights provided by the fclayer object.
ffnet = td.Function(fclayer) >>  td.Function(fclayer) >> td.Function(fclayer)

>>演算子は、関数の構成を示します。

Python operations

td.InputTransformブロックは、任意のPython関数をブロックにラップします。 Pythonオブジェクトを入力として受け取り、Pythonオブジェクトを出力として返します。たとえば、次のブロックはPython文字列を浮動小数点数のリストに変換します。

td.InputTransform(lambda s: [ord(c)/255.0 for c in s])

その名前が示すように、InputTransformは、主にPythonで入力データを前処理してTensorFlowに渡すために使用されます。データがパイプラインのTensorFlow部分に到達すると、データ上で任意のPythonコードを実行することはできなくなります.Foldはそのような試みに対して型エラーを生成します。

Block composition

ブロックは、より複雑な振る舞いを持つブロックを作成するために、さまざまな方法で他のブロックと合成することができます。

Wiring blocks together

最も簡単な構成は、>>演算子を使用して、あるブロックの出力を別のブロックの入力に配線することです。構文f >> gは関数の構成を示します。入力をfに送り、fの出力をgに渡し、gの出力を返す新しいブロックを作成します。

例えば、次のブロックはMNISTモデルの一部であり、MNIST画像は長さ784の文字列として直列化されて格納される。各文字列を浮動体のリストに変換し、リストをテンソルに変換し、接続された層。

mnist_model = (td.InputTransform(lambda s: [ord(c) / 255.0 for c in s]) >>
               td.Vector(784) >>             # convert python list to tensor
               td.Function(td.FC(100)) >>    # layer 1, 100 hidden units
               td.Function(td.FC(100)))      # layer 2, 100 hidden units

Dealing with sequences

フォールドは、マップやフォールドなどの高次関数に類似するブロックを使用してデータのシーケンスを処理します。配列は任意の長さとすることができ、長さが例から実施例に変わることがある。シーケンスをあらかじめ定義された長さに切り捨てるか埋め込む必要はありません。
  • td.Map(f):シーケンスを入力として受け取り、ブロックfをシーケンス内のすべての要素に適用し、シーケンスを出力として生成します。
  • td.Fold(f、z):シーケンスを入力として受け取り、ブロックzの出力を初期要素として使用して左折を実行します。
  • td.RNN(c):リカレントニューラルネットワーク。マップとフォールドの組み合わせです。初期状態と入力シーケンスをとり、rnn-cell cを使用して新しい状態と前の状態と入力からの出力を生成し、最終状態と出力シーケンスを返します。
  • td.Reduce(f):シーケンスを入力として1つの値に減らします.fを要素に対して適用し、本質的にfでバイナリ式のツリーを実行します。
  • td.Zip():シーケンスのタプルを入力として受け取り、一連のタプルを出力として生成します。
  • td.Broadcast(a):ブロックaの出力を取り出し、無限の繰り返しシーケンスに変換します。通常、ZipとMapと一緒に使用され、aを使用する関数でシーケンスの各要素を処理します。
サンプル
# Convert a python list of scalars to a sequence of tensors, and take the
# absolute value of each one.
abs = td.Map(td.Scalar() >> td.Function(tf.abs))

# Compute the sum of a sequence, processing elements in order.
sum = td.Fold(td.Function(tf.add), td.FromTensor(tf.zeros(shape)))

# Compute the sum of a sequence, in parallel.
sum = td.Reduce(td.Function(tf.add))

# Convert a string to a vector with a character RNN, using Map and Fold
char_rnn = (td.InputTransform(lambda s: [ord(c) for c in s]) >>
            # Embed each character using an embedding table of size 128 x 16
            td.Map(td.Scalar('int32') >>
                   td.Function(td.Embedding(128, 16))) >>
            # Fold over the sequence of embedded characters,
            # producing an output vector of length 64.
            td.Fold(td.Concat() >> td.Function(td.FC(64)),
                    td.FromTensor(tf.zeros(64))))

FoldブロックとRNNブロックは、LSTMのようなシーケンスモデルをデータのリストに適用するために使用できます。 1つの注意点として、非常に長いシーケンスに対する逆伝播には依然として勾配の問題が残っているため、Reduceを使用すると、シーケンスの長さの対数であるReduceを使用することができます。

さらに、長いシーケンスを処理している間にTensorFlow自体がメモリ不足になる可能性があります。すべての中間結果をメモリに保存してグラデーションを計算する必要があるためです。したがって、Foldはシーケンス長に制限を設けませんが、td.InputTransformを使用して長いシーケンスを管理可能な長さに切り捨てることが依然として望ましい場合があります。

Dealing with records

レコードは、それぞれがPython辞書やprotobufメッセージなどの異なる型を持つ名前付きフィールドのセットです。 td.Recordブロックはレコードを入力として受け取り、子ブロックを各フィールドに適用し、その結果をタプルに結合して出力として生成します。出力タプルは、出力ベクトルを得るためにtd.Concat()に渡すことができます。

たとえば、次のブロックは3つのフィールドのレコードを128次元のベクトルに変換します。これは、埋め込みテーブルを使用してidフィールドをベクトルに変換し、上で定義した文字RNNを使用して名前フィールドを実行し、その位置フィールドをそのまま扱い、3つの結果を連結して完全連結レイヤーに渡します。

# Takes as input records of the form:
# {'id':       some_id_number,
#  'name':     some_string,
#  'location': (x,y)
# }
rec = (td.Record([('id', td.Scalar('int32') >>
                         td.Function(td.Embedding(num_ids, embed_len))),
                  ('name', char_rnn),
                  ('location', td.Vector(2))]) >>
       td.Concat() >> td.Function(td.FC(128)))

完全に接続されたレイヤーは128個の隠れた単位を持ち、長さ128のベクトルを出力します。これは、recがコンパイルされたときの入力(embed_len + 64 + 2)のサイズを推測します。

Wiring things together, in more complicated ways.

>>演算子を使用した単純な関数の構成は、標準のUnixパイプに似ています。通常は、大抵の場合、特に入力データ構造を横断するRecordやFoldのようなブロックと組み合わせると十分です。

しかし、いくつかのモデルでは、より複雑な方法で物事を結ぶ必要がある場合があります。 td.Compositionブロックでは、子の入力と出力を任意のDAGにまとめることができます。たとえば、次のコードでは、LSTMセルをブロックとして定義しています。これは、前述のRNNブロックでの使用に適しています。 lstm_cell.scope()内で定義されたすべてのブロックは、lstm_cellの子になります。 b.reads(...)メソッドは、別のブロックまたはブロックのタプルの出力をbの入力に結び付け、関数適用にほぼ対応します。 bの出力がタプルの場合、b [i]構文を使用してタプルの個々の要素を選択することができます。

# The input to lstm_cell is (input_vec, (previous_cell_state, previous_output_vec))
# The output of lstm_cell is (output_vec, (next_cell_state, output_vec))
lstm_cell = td.Composition()
with lstm_cell.scope():
  in_state = td.Identity().reads(lstm_cell.input[1])
  bx = td.Concat().reads(lstm_cell.input[0], in_state[1])     # inputs to gates
  bi = td.Function(td.FC(num_hidden, tf.nn.sigmoid)).reads(bx)  # input gate
  bf = td.Function(td.FC(num_hidden, tf.nn.sigmoid)).reads(bx)  # forget gate
  bo = td.Function(td.FC(num_hidden, tf.nn.sigmoid)).reads(bx)  # output gate
  bg = td.Function(td.FC(num_hidden, tf.nn.tanh)).reads(bx)     # modulation
  bc = td.Function(lambda c,i,f,g: c*f + i*g).reads(in_state[0], bi, bf, bg)
  by = td.Function(lambda c,o: tf.tanh(c) * o).reads(bc, bo)    # final output
  out_state = td.Identity().reads(bc, by)   # make a tuple of (bc, by)
  lstm_cell.output.reads(by, out_state)

この定義は、説明目的でのみ提供されています。セルの本体は対応するTensorFlow操作をラップするファンクションブロックのみで構成されているため、LSTMセルをコンポジションではなく単純なレイヤーとして直接実装する方がクリーンで効率的です。

Recursion and forward declarations

ツリー再帰的ニューラルネットワークを実装するには、再帰的ブロック定義が必要です。ブロックの型は、最初にtd.ForwardDeclarationで宣言されます。その後、ブロック自体は、順方向宣言を使用して再帰的参照を作成することで通常どおりに定義されます。最後に、td.ForwardDeclaration.resolve_toを呼び出すと、再帰的定義が前方宣言に結び付けられます。

たとえば、TensorFlowを使用して算術式を評価するブロックを次に示します。

# the expr block processes objects of the form:
# expr_type ::=  {'op': 'lit', 'val': <float>}
#             |  {'op': 'add', 'left': <expr_type>, 'right': <expr_type>}
expr_fwd = td.ForwardDeclaration(pvt.PyObjectType(), pvt.Scalar())
lit_case = td.GetItem('val') >> td.Scalar()
add_case = (td.Record({'left': expr_fwd(), 'right': expr_fwd()}) >>
            td.Function(tf.add))
expr = td.OneOf(lambda x: x['op'], {'lit': lit_case, 'add': add_case}) 

expr_fwd.resolve_to(expr)expr_fwdは宣言であり、ブロックではありません。 expr_fwd()を呼び出すたびに、宣言を参照するブロックが作成されます。

コメント

このブログの人気の投稿

マルレク・ネット「エントロピーと情報理論」公開しました。

初めにことばありき

人間は、善と悪との重ね合わせというモデルの失敗について