Translate

2017年11月21日火曜日

TensorFlowにおけるフィーチャ列はこうしてね的なブログ記事を翻訳してみた


TensorFlow 1.4.0 になってイロイロガチャガチャしている。

一番ビックリしたのがTutorialのSequence-to-Sequence。
イキナリブログ記事のリンクだけになっていた..

Sequence-to-Sequence Models
https://www.tensorflow.org/tutorials/seq2seq

以下翻訳した文章。
-----

Sequence-to-Seqyuence モデル


最新の Tensorflow API を使用してシーケンス間モデルを構築するための TensorFlow ニューラルマシン翻訳チュートリアルをご覧ください。

-----

相当互換性無視して変えちゃったものだから、
TensorFlowの研修とかやっている人たちも大変だ..


それもこれも
TensorFlow 1.4.0 が悪いんや...
 


..と嘆いていても始まらないので
今日もチクチク勉強をした。

今日読んだのがコレ。

Google Developers
Introducing TensorFlow Feature Columns
https://developers.googleblog.com/2017/11/introducing-tensorflow-feature-columns.html

タイトルを訳すと、
TensorFlow フィーチャ列の紹介

ようは、Estimatorになりinput_fnで入力データを投入するようになったのだけど
その入力データの要素をフィーチャ(特徴)とよんでおり
事前処理でこうしておけよ、今あるEstimatorはこれこれこの型式でしか受け入れねえからよ
という意味合いがあるようだ。

前置きはさておいて、翻訳した本文を載せておく。

当然このブログ上の翻訳はすべてat your own risk で参照のこと。
-----
2017年11月20日(月)
TensorFlowチームによる投稿

TensorFlow Datasets および Estimators を紹介するブログシリーズの第2部へようこそ。この記事では、フィーチャ列(feature columns)として、Estimator の訓練や推論に必要な特徴をあらわすデータ構造を取り上げています。フィーチャ列は非常に豊富で、さまざまなデータを表現できます。
ではパート1にはいりましょう。我々は、既製の Estimator である DNNClassifier を使用し、4つの入力フィーチャ列からアイリスの花の種類を予測するモデルを訓練することとします。このサンプルでは、数値フィーチャ列のみを作成します(tf.feature_column.numeric_column)。フィーチャ列は花弁と萼片の長さをモデル化するのに十分でしたが、実世界のデータセットには当然花以外のすべての種類・類別に対する数値以外のフィーチャ列も含まれています:
数値以外のフィーチャタイプをどのように表現できますか?それはまさにこのブログ記事で取り上げようとしていることなのです。

ディープニューラルネットワークへの入力

どのような種類のデータをディープニューラルネットワークに実際に送り込むことができるかを尋ねることから始めましょう。答えは、もちろん数字です(たとえば、 tf.float32)。実際には、ニューラルネットワークのすべてのニューロンは、重みおよび入力データに対して乗算および加算演算を実行しています。しかし、現実の入力データには、たとえば product_class(製品クラス)といった非数値(カテゴリ)のデータが含まれることがよくあります。次の3つの非数値値を含むことができる機能を考えてみましょう:
  • キッチン用品
  • エレクトロニクス
  • スポーツ
一般的な機械学習モデルでは、1 が値の存在を表し、0が値の不存在を表す単純なベクトルとして分類値を表します。たとえば、製品クラス(product_class) を スポーツ に設定すると、機械学習モデルは通常 product_class [0, 0, 1]のようになります。
  • 0:キッチン用品、存在しません
  • 0:エレクトロニクス、存在しません
  • 1::スポーツ、存在します
従って、row データは数値またはカテゴリにすることができるので、機械学習モデルはすべてのフィーチャを数値またはベクトルのいずれかとして表しています。


フィーチャ列の紹介

図2 が示すような feature_columns に、Estimator(DNNClassifierIrisの場合)の引数を使用して、モデルへの入力を指定します。フィーチャ列は、入力したデータを input_fnモデルで橋渡しします。
フィーチャをフィーチャ列として表すには、tf.feature_column パッケージの関数を呼び出します。この記事は、このパッケージの9つの機能について説明しています。図3に示すように、9つの関数 bucketized_column は両方とも、Categorical-Column オブジェクトまたは Dense-Column オブジェクトを返します。ただし、両方のクラスから継承します。
 これらの機能を詳しく見てみましょう。

数値列

アイリス分類器はすべての入力フィーチャ(SepalLength(花弁長)、 SepalWidth(花弁幅)、 PetalLength(萼片長)、 PetalWidth(萼片幅))のために tf.numeric_column()を呼び出しました。 tf.numeric_column()はオプションの引数を提供しますが、引数を指定せずに関数を呼び出すことは、デフォルトのデータ型(tf.float32)で数値をモデルに入力するための完全な簡単な方法です。たとえば:
# デフォルトは tf.float32 スカラーになる
numeric_feature_column = tf.feature_column.numeric_column(key="SepalLength")

引数 dtype を使ってデフォルト以外の数値データ型を指定します。たとえば:
# tf.float64 スカラーへ再表現
numeric_feature_column = tf.feature_column.numeric_column(key="SepalLength",
                                                          dtype=tf.float64)

デフォルトでは、数値列は単一の値(スカラー)を作成します。引数shapeを使って,別の型を指定します。たとえば:
# Represent a 各セルが tf.float32 を含む10-要素ベクトルへ再表現
vector_feature_column = tf.feature_column.numeric_column(key="Bowling",
                                                         shape=10)

# 各セルに tf.float32 スカラを含む 10x5 行列へ再表現
matrix_feature_column = tf.feature_column.numeric_column(key="MyMatrix",
                                                         shape=[10,5]) 



バケット化カラム

多くの場合、モデルに直接数値を入力するのではなく、その数値を数値範囲に基づいて異なるカテゴリに分割します。これを行うには、バケット化された列を作成します。たとえば、家が建てられた年を表す生データを考えてみましょう。その年をスカラー数値列として表す代わりに、次の4つのバケットに年を分けることができます。 
 モデルは、バケットを次のようにあらわします。

数値を完全に有効な入力としてモデルに分割すると、なぜこのようなカテゴリの値になりますか?カテゴリ化は1つの入力番号を4要素ベクトルに分割することに注意してください。したがって、モデルは単なる1つではなく4つの個別の重みを学習できるようになりました。4つの重みは、1つより豊かなモデルを作成します。さらに重要なことは、バケット化は、要素のうちの1つだけが設定され(1)、他の3つの要素がクリア(0)されるため、モデルが異なる年のカテゴリを明確に区別できるようにします。単一の数字(1年)を入力として使用すると、モデルはカテゴリを区別できません。そのため、バケットはモデルに学習に使用できる重要な情報を追加します。

次のコードは、バケット化されたフィーチャを作成する方法を示しています。
# raw 入力データの数値カラム
numeric_feature_column = tf.feature_column.numeric_column("Year")

# 1960年、1980年、2000年を表す数値カラムのバケット化
bucketized_feature_column = tf.feature_column.bucketized_column(
    source_column = numeric_feature_column,
    boundaries = [1960, 1980, 2000])

次の点に注意してください。
  • バケット化された列を作成する前に、最初に生の年を表す数値列を作成しました。
  • 数値列を最初の引数として tf.feature_column.bucketized_column() へ渡しました。
  • 3要素 boundaries ベクトルを指定すると、4要素バケット化ベクトルが作成されます 。


カテゴリID列

カテゴリID列は、バケット化された列の特殊なケースです。伝統的なバケット化された列では、各バケットは値の範囲を表し ます(たとえば、1960年から1979年まで)。カテゴリ識別列では、各バケットは単一の一意の整数を表します。たとえば、整数範囲 [0, 4] を表すとします。(つまり、整数 0, 1, 2, または 3 を表すとします)この場合、カテゴリIDマッピングは次のようになります。

なぜカテゴリID列として値を表現したいのでしょう?バケット化された列の場合と同様に、モデルはカテゴリ型ID列の各クラスの個別の重みを学習できます。たとえば、文字列を表現するために文字列を使用する代わりに、product_class の各クラスを固有の整数値で表現してみましょう:
  • 0="kitchenware"
  • 1="electronics"
  • 2="sport"

カテゴリID列を実装するために tf.feature_column.categorical_column_with_identity() を呼び出します。たとえば:
#入力 "feature_name_from_input_fn" のカテゴリ化された出力を作成。
# これは値は 0 以上 num_backets 未満となる整数でなければならない。
identity_feature_column = tf.feature_column.categorical_column_with_identity(
    key='feature_name_from_input_fn', 
    num_buckets=4) # Values [0, 4)

# 上記の 'feature_name_from_input_fn' は、input_fn(以下を参照)から
# 返される整数キーと一致する必要があります。
# この場合 'Integer_1' または 'Integer_2' は 'feature_name_from_input_fn'
# の代わりに有効な文字列になります。
# 詳細は、このブログシリーズの第1部を参照してください。
def input_fn():
    ......
    return ({ 'Integer_1':[values], ...., 'Integer_2':[values] },
            [Label_values])


カテゴリ語彙カラム

文字列をモデルに直接入力することはできません。代わりに、文字列を数値またはカテゴリの値にマップする必要があります。カテゴリ化された語彙列は、文字列をone-hotベクトルとして表現する良い方法です。たとえば:

ご覧のように、カテゴリ語彙カラムは、カテゴリIDカラムの列挙型です。TensorFlowには、カテゴリ別の語彙列を作成するための2つの異なる機能があります。

この tf.feature_column.categorical_column_with_vocabulary_list() 関数は、明示的な語彙リストに基づいて各文字列を整数にマップします。たとえば:
# 文字列である入力 "feature_name_from_input_fn" 与えられた場合、
# 入力を語彙リストの要素の1つにマッピングすることによって、
# モデルにカテゴリ的なフィーチャを作成する
vocabulary_feature_column =
    tf.feature_column.categorical_column_with_vocabulary_list(
        key="feature_name_from_input_fn",
        vocabulary_list=["kitchenware", "electronics", "sports"]) 

前述の関数には重大な欠点があります。つまり語彙リストが長い場合はタイピングが多すぎます。このような場合は、代わりに tf.feature_column.categorical_column_with_vocabulary_file() を呼び出すことで、語彙を別のファイルに配置できます。たとえば:
# 文字列である入力 "feature_name_from_input_fn"が与えられた場合、
# 入力を語彙ファイル内の要素の1つにマッピングすることによって、
# モデルに対するカテゴリ化されたフィーチャを作成する
vocabulary_feature_column =
    tf.feature_column.categorical_column_with_vocabulary_file(
        key="feature_name_from_input_fn",
        vocabulary_file="product_class.txt",
        vocabulary_size=3)

# product_class.txtには、次のような場合には1行の語彙要素が必要
kitchenware
electronics
sports


ハッシュバケットを使ったカテゴリ制限

これまでのところ、私たちは素朴な数のカテゴリで作業してきました。たとえば、この product_class の例では 3 つのカテゴリしかありません。しかし、しばしばカテゴリの数が膨大になり、それぞれの語彙や整数に個別のカテゴリを持たせることができないため、あまりにも多くのメモリを消費することになります。これらのケースでは、代わりに質問を回して「どのくらいの数のカテゴリを入力してもらえますか」と尋ねることができます。実際、この tf.feature_column.categorical_column_with_hash_buckets() 関数を使用するとカテゴリの数を指定できます。たとえば、次のコードは、この関数が入力のハッシュ値を計算し、モジュロ演算子を使用して hash_bucket_size 個のカテゴリの1つに配置する方法を示しています。
# 入力 "feature_name_from_input_fn" のカテゴリ出力を作成
# カテゴリは次のように:hash_value("feature_name_from_input_fn") % hash_bucket_size
hashed_feature_column =
    tf.feature_column.categorical_column_with_hash_bucket(
        key = "feature_name_from_input_fn",
        hash_buckets_size = 100) # The number of categories

この時点で、あなたは正しいことを考えるかもしれません: "これは狂っています!" 結局のところ、異なる入力値をより小さなカテゴリのセットにフォーカスしています。これは、恐らく完全に無関係な2つの入力が同じカテゴリにマッピングされることを意味し、結果的にニューラルネットワークにおいても同様の意味となります。図 7 は、このジレンマを示しており、 kitchenware および sports の両方がカテゴリ(ハッシュバケット)12 に割り当てれています:
 機械学習の多くの直感的な現象と同様に、実際にはハッシュがよく機能することがよくあります。これは、ハッシュカテゴリがモデルにある程度の分離を提供するためです。このモデルは、さらに kitchenware から sports を分離するための追加機能を使用することができます。


フィーチャクロス

ここで最後に取り上げるのは、複数の入力機能を1つにまとめることができるカテゴリの列です。それは フィーチャクロス(feature crosses)という名で知られており、フィーチャを組み合わせることにより、そのフィーチャコンビネーションが何を意味するものであっても、モデルは個別のウェイトを具体的に学習できます。

より具体的な例としてここでは、ジョージア州アトランタの不動産価格をモデルで計算したいとします。この都市の不動産価格は場所によって大きく異なります。緯度と経度を別々のフィーチャとして表現することは、不動産の場所の依存関係を特定する上であまり役に立ちません。ただし、緯度と経度を1つのフィーチャに渡すと、位置を特定できます。アトランタを100×100の長方形セクションのグリッドとして表現し、10,000個のセクションのそれぞれをその緯度と経度の交差点で識別するとします。このクロスはモデルが個々のセクションに関連する価格設定条件を拾うことを可能にします。これは緯度と経度だけよりもはるかに強いシグナルです。

図8は、都市のコーナーの緯度と経度の値を示す私たちの計画を示しています。
この解決法については、これまでに見てきたいくつかのフィーチャ列と tf.feature_columns.crossed_column() 関数を組み合わせて使用しました 。
# input_fn関数で入力緯度・経度をそれぞれ範囲 [0, 100) の整数値に変換
def input_fn():
    # データセットを使用して、経度と緯度の入力値を読み取る
    latitude = ...   # tf.float32 型の値
    longitude = ...  # tf.float32 型の値

    # この例では、lat_int, long_int のフィーチャ列を返却
    # 完全なプログラムの辞書はおそらくより多くのキーを持つ
    return { "latitude": latitude, "longitude": longitude, ...}, labels

# マップからわかるように、緯度範囲 [33.641336, 33.887157] を100個の
# バケットに分割したいと考えている。これを行うには、np.linspace を使用して、
# この範囲の min と max の間の 99 個の数値のリストを取得する。
# このリストを使用して、緯度を 100個のバケット化が可能となる。
latitude_buckets = list(np.linspace(33.641336, 33.887157, 99))
latitude_fc = tf.feature_column.bucketized_column(
    tf.feature_column.numeric_column('latitude'),
    latitude_buckets)

# 緯度同様、経度のバケット化を実行
longitude_buckets = list(np.linspace(-84.558798, -84.287259, 99))
longitude_fc = tf.feature_column.bucketized_column(
    tf.feature_column.numeric_column('longitude'), longitude_buckets)

# fc_longitude x fc_latitude のクロスフィーチャを作成
fc_san_francisco_boxed = tf.feature_column.crossed_column(
    keys=[latitude_fc, longitude_fc],
    hash_bucket_size=1000) # 正確なルールはない、1,000バケットという数は良いのだろうか?
以下のいずれかからクロスを作成することができます:
  • フィーチャ名; つまり、input_fnから返却されたdictからの名前。
  • categorical_column_with_hash_bucketを覗いたカテゴリ化カラム(図3を参照のこと) 。

フィーチャ列 latitude_fc longitude_fc をクロスさせると、TensorFlow は以下のように構成された (latitude_fc, longitude_fc) の 10,000 個の組み合わせを作成します:
(0,0),(0,1)...  (0,99)
(1,0),(1,1)...  (1,99)
…, …,          ...
(99,0),(99,1)...(99, 99)
この関数 tf.feature_column.crossed_column は、これらの組み合わせに対してハッシュ計算を実行し、hash_bucket_size でモジュロ演算を実行して結果をカテゴリに入れます。前に説明したように、ハッシュ関数とモジュロ関数を実行すると、おそらくカテゴリの衝突が発生します。つまり、複数(緯度、経度)のフィーチャクロスが同じハッシュバケットになります。実際には、フィーチャクロスを実行することは、依然としてモデルの学習能力に大きな価値をもたらします。

やや直感的には、フィーチャクロスを作成するときは、元の(クロスしていない)フィーチャをモデルに含める必要があります。例えば、 (latitude, longitude) フィーチャクロスだけでなく、 latitude および longitude を別個のフィーチャ列として提供します。別個に提供したフィーチャ列 latitude 及びlongitude は、モデルが異なるフィーチャクロスを含むハッシュバケットの内容物を分離するのに役立ちます。

これに関する完全なコードサンプルについては、このリンクを参照してください。また、この記事の末尾にある参照セクションでは、フィーチャクロスのサンプルがさらにたくさんあります。


インジケータ列と埋め込み列

インジケータ列と埋め込み列は、フィーチャに直接作用することはありませんが、代わりにカテゴリ型列を入力として使用します。

インジケータ列を使用しているときは、TensorFlowに、product_class サンプルで見たことを正確に行うように指示しています。つまり、 インジケータ列は、各カテゴリをone-hotベクトルの要素として扱います。一致するカテゴリは値 1 とし、残りは 0 となります。


インジケータ列 を作成する方法は次のとおりです。
categorical_column = ... # 任意のカテゴリ化列を作成、 図3を参照のこと

# カテゴリ列をインジケータ列として表す
# これはカテゴリ毎に1つの要素を持つone-hotベクトルを作成することを意味している
indicator_column = tf.feature_column.indicator_column(categorical_column)

さて、クラスが3つしかないのではなく、100万..もしくは10億あるとします。こうなると、カテゴリの数が増えるにつれてインジケータ列を使いニューラルネットワークを訓練することは不可能になります。

この制限を克服するために埋め込み列を使用できます。 埋め込み列は、データを多くの次元の one-hot ベクトルとして表現する代わりに、そのデータを、各セルが 0 または 1 だけでなく、任意の数を含むことができる低次元の通常のベクトルとして表します。埋め込み列には、インジケータ列よりもはるかに少ないセルが含まれています。

インジケータ列と埋め込み列を比較する例を見てみましょう。私たちの入力サンプルが、わずか 81 語の限られたパレットとは異なる言葉で構成されているとします。さらに、データセットが提供するものが、4つの別々のサンプルで以下の入力語を提供すると仮定します:
  • "dog"
  • "spoon"
  • "scissors"
  • "guitar"
ここで 図10は、列またはインジケータ列を埋め込むための処理経路を示しています。

例が処理されると、categorical_column_with... 関数の1つが例の文字列を数値のカテゴリ値にマップします。たとえば、関数は"spoon"を [32] にマップします(32という数値は私たちの想像です。実際の値は、マッピング機能によって異なります)。あなたは、次の2つの方法のいずれかでこれらの数値カテゴリ値を表すことができます。
  • インジケータ列として。関数は、カテゴリ値(0, 32, 79, 80)のインデックスに 1 を、他のすべての位置に 0 を設定して、各数値カテゴリ値を 81 要素ベクトルに変換します(パレットは81語で構成されているため)。
  • 埋め込み列として。関数は、数値カテゴリ値 (0, 32, 79, 80) をルックアップテーブルのインデックスとして使用します。そのルックアップテーブルの各スロットは、3要素ベクトルを含みます。
埋め込みベクトルの値はどのようにして魔法のように割り当てられますか?実際には、課題はトレーニング中に発生します。つまり、モデルは問題を解決するために入力数値のカテゴリ値を埋め込みベクトル値にマップする最良の方法を学習します。組み込みベクトルはトレーニングデータからカテゴリ間の新しい関係を学習するため、列を埋め込むことでモデルの機能が向上します。

この例で埋め込みベクトルのサイズが 3 である理由は何ですか?さて、次の「数式」は、埋め込みディメンションの数に関する一般的な経験則を提供します。
embedding_dimensions =   number_of_categories ** 0.25
すなわち、埋め込みベクトル次元は、カテゴリ数の4乗根でなければなりません。この例の語彙サイズは81であるため、推奨されるディメンションの数は3です。
3 = 81 ** 0.25
これは単なる一般的なガイドラインであることに注意してください。必要に応じて埋め込み寸法の数を設定することができます。

embedding_column を作成するために tf.feature_column.embedding_column を呼び出します。埋め込みベクトルの次元は、上で説明した手元の問題に依存しますが、一般的な値は、3まで、300まで、あるいはそれ以上にまで低くなります:
categorical_column = ... # 図3 にあるようなカテゴリ化カラムを作成

# Represent the categorical column as an embedding column.
# This means creating a one-hot vector with one element for each category.
# カテゴリ型列を埋め込み列として表す
# これはカテゴリ毎に1つの要素を持つ one-hot ベクトルを作成することを意味する
embedding_column = tf.feature_column.embedding_column(
    categorical_column=categorical_column,
    dimension=dimension_of_embedding_vector)
埋め込みは機械学習の大きなテーマです。この情報は、フィーチャ列として使用するためのもので、簡単に説明していますので、詳細について知りたい方は、この記事の最後を参照してください。


フィーチャ列をEstimatorへ渡す

まだあるのですか?と思っている方も多いと思いますが、フィーチャ列の基礎を卒業する前にあと少しだけ残っています。

図1を見直してもらえばわかりますが、フィーチャ列は入力データ(input_fnから返されたフィーチャ辞書によって提供)をモデルに入力された値にマッピングします。フィーチャ列を feature_columns 推定値の引数リストとして指定します。feature_columns 引数は、Estimatorによって異なることに注意してください:
・フィーチャ列のすべてのタイプを受け入れる。
dense columnのみ受け入れる(図3を参照のこと)。他の列タイプは、前述のように indicator_column または embedding_column にラップする必要がある。
LinearClassifier および LinearRegressorのように、引数 linear_feature_columns はどんな列タイプも受け入れ可能。
・引数 dnn_feature_columns、しかしながら(DNNClassifier DNNRegressor のように) dense columnに制限。

上記のルールの理由は、この入門記事の範囲を超えているため解説しませんが、今後のブログ記事では必ずカバーしていきたいと思います。


サマリ

フィーチャ列を使って入力データをモデルにフィードする表現にマップします。このシリーズの第1回では numeric_column のみ使用しましたが、この記事で説明している他の機能を使用すると、他の機能の列を簡単に作成できます。

フィーチャ列の詳細については、次の記事をご覧ください:

もしあなたが埋め込みに関してより詳しく知りたい場合は、次の記事を御覧ください:
-----
今用意されているEstimatorの具現クラス..とPythonでもいうのかはわからないけど
へのフィーチャ列にはある程度決まりがあるみたいで
Denseでないとだめな列もあったりするのね..

..埋め込み列迄書かれてあるのを見て、
すかさずチュートリアルのWord Embeddingsのページに行ったが
ここはまだTensorFlow本家からは動いてないみたいでホッとした...
語彙リストがなんで単語のリストなのかもなんとなくわかったけど
やっぱり Sequence-to-Sequence が気になるなあ..










0 件のコメント:

既存アプリケーションをK8s上でコンテナ化して動かす場合の設計注意事項メモ

既存アプリをK8sなどのコンテナにして動かすには、どこを注意すればいいか..ちょっと調べたときの注意事項をメモにした。   1. The Twelve Factors (日本語訳からの転記) コードベース   バージョン管理されている1つのコードベースと複数のデプロイ 依存関係 ...