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 が気になるなあ..










2017年11月15日水曜日

TensorFlow Lite が発表された件

今朝のタイムラインには驚いた。



リンク先のブログ記事をとりあえず勝手に翻訳してみた。
以下、その翻訳文です。
#参照する人は at your own risk でお願いします。


-----

TensorFlow Lite を発表





本日、我々は TensorFlow モバイルと組み込み機器向けの  軽量ソリューションである TensorFlow Lite の開発者向けプレビューを発表します。


TensorFlowは、サーバのラックから小型のIoTデバイスまで、常に多くのプラットフォーム上で動作していますが、機械学習モデルの採用が過去数年間で急激に増加しているため、モバイルデバイスや組み込みデバイスに展開する必要があります。TensorFlow Liteを使用すると、デバイス上の機械学習モデルのレイテンシを低く抑えることができます。

TensorFlow Lite は、次のように設計しています:

  • 軽量 バイナリサイズが小さく、初期化/起動が速いオンデバイスマシン学習モデルの推論を可能にする
  • クロスプラットフォーム AndroidやiOSの複数のプラットフォームで動作 するように設計されたランタイム
  • 高速 モバイルデバイスに最適化され、モデルの読み込み時間が大幅に短縮、ハードウェアアクセラレーションをサポート
今日、ML ワークロードをより効率的に処理するための専用ハードウェアを組み込んだモバイルデバイスが続々と登場しています。TensorFlow Liteは、Android ニューラルネットワークAPI をサポートし、これらの新しいアクセラレータを利用できるようにします。
 

ハードウェアのアクセラレータが利用できない場合、 TensorFlow Lite は最適化された CPU 実行に戻ります。これにより、モデルが大量のデバイスで高速に動作することが保証されます。
 

アーキテクチャ

 次の図は、TensorFlow Lite のアーキテクチャ設計を示しています。


 個々のコンポーネントは次のとおりです:


  • TensorFlow Model:ディスクに保存された訓練された TensorFlow モデル
  • TensorFlow Lite コンバータ:モデルをTensorFlow Liteファイル形式に変換するプログラム
  • TensorFlow Liteモデルファイル:最大速度と最小サイズに最適化された FlatBuffers に基づくモデルファイル形式
TensorFlow Liteモデルファイルは、モバイルアプリケーション内にデプロイされます。モバイル・アプリケーション内では:


  • Java API: Android 上のC++ APIに関する便利なラッパー
  • C++ API:TensorFlow Liteモデルファイルを読み込み、Interpreterを呼び出す。AndroidとiOSの両方で同じライブラリが利用可能
  • Interpreter:Operatorの集合を使ってモデルを実行する。Interpreter は限られたOperatorのみローディングをサポートします:Operatorなしで70KB、すべてのOperatorをロードして300KBです。TensorFlowが必要とする1.5M (通常のOperatorの集合を使用)から大幅に削減
  • 限られたAndroidデバイスでは、InterpreterはAndroid Neural Networks APIを使用してハードウェアアクセラレーションを行い、使用できない場合はCPUの実行をデフォルトにする

開発者は、インタプリタで使用できるC ++ APIを使用してカスタムカーネルを実装することもできます。
 


モデル

TensorFlow Lite は、すでに訓練され、モバイル向けに最適化された多数のモデルをサポートしています。


  • MobileNet:モバイルおよび組み込み機器での効率的な実行のために特別に設計された1000種類のオブジェクトクラスを識別できるビジョンモデルクラス
  • Inception v3:MobileNetと機能的に類似しているが、より高い精度を提供するが、より大きなサイズを有する画像認識モデル
  • Smart Reply:着信した会話型チャットメッセージにワンタッチで返信するオンデバイスの会話モデル。ファーストパーティとサードパーティのメッセージングアプリはAndroid Wearでこの機能を使用する。
Inception v3 と MobileNets は ImageNet データセットで訓練されています。転移学習(Transfer Learning)を行うことで、自分の画像データセットでこれらを簡単に再学習することができます。
 


TensorFlow Mobile はいかが?

ご存じのように、TensorFlowはすでに、 TensorFlow Mobile API を通じてモデルのモバイルおよび組み込み展開をサポートしています。今後、 TensorFlow Lite は TensorFlow Mobile の進化と見なされるべきです。成熟するにつれて、TensorFlow Mobile はモバイルデバイスや組み込みデバイスにモデルを導入するための推奨ソリューションになるでしょう。この発表により、TensorFlow Lite は開発者のプレビューとして利用可能になり、TensorFlow Mobileはまだプロダクションアプリをサポートします。

TensorFlow Liteの範囲は大きく、依然として積極的に開発中です。この開発者プレビューでは、最も重要な共通モデルの一部でパフォーマンスを保証するために、意図的に制約のあるプラットフォームを使用しています。私たちは、ユーザのニーズに基づいて将来の機能拡張を優先させる予定です。継続的な開発の目標は、開発者の経験を簡素化し、モバイルデバイスや組み込みデバイスのモデル展開を可能にすることです。

開発者が TensorFlow Lite に手を差し伸べていることに、私たちは興奮しています。私たちは、TensorFlowプロジェクトの残りの部分と同じ強度で外部コミュニティをサポートし、取り組むことを計画しています。TensorFlow Lite で何ができるのかを待つことはできません。

詳細については、TensorFlow Liteのドキュメントページを参照してください。
より多くのアップデートをお待ちしております。

ハッピー TensorFlow Liteコーディング!

------

実は、転移学習でカスタマイズした学習済みモデルをKeras.js で縮退させたモデル使ってHTML5アプリにしてうごかしてやろうと思っていたら..

そうか..Andoroid系のハードにGPUリソース入れる気なんだ..

..でそれでアクセラレーションした機械学習モデルネイティブアプリ動かして
クラウド上のコグニティブとか抜かしているAPIサービス群をまるっとぶっ飛ばす気だ..




...やられた..コカコーラの記事でなんとなく怪しいとは思ってたんだよな..

 そうなると..Appleは対抗でiPhone/iPadにGPUを入れてくるよな..

..モバイルGPU市場の株式アツくなるかもしれないなあ..

2017年11月10日金曜日

tf.dataを制するものはTensorFlow1.4.0 を制す、な話

前に書いた記事「TensorFlow 1.4.0 rc0 がリリースされていた
にでてきたとおり、

TensorFlow 1.4.0 リリースの核
 Estimator
であるのは明白だ。

いろんな機械学習モデルの実装が
Estimator としてこれから提供されるだけでなく
学習済み機械学習モデルは
缶詰めされた Esatimator (Canned Estimator)としても提供される。


すぐに鎮火すると思っっていた人工知能ブームだったが、
自動運転やチャットボットとかの台頭と
顧客がいまだ何に使えるかわかっていない状態が続き
幻滅期に一気に移行しなかったようで、
そうこうしているうちに
コグニティブ API 流用レベルのいっちょかみSIer
にも
私のように勉強する時間ができ
TensorFlowやChainer、Theano、Kerasなどの
ライブラリを使った機械学習いっちょかみSIer 
へレベルが徐々にあがってきた。

この「機械学習いっちょかみSIer」が最近
いまだ何に使えるかわかっていない顧客」に
学習済み機械学習モデルを流用して類似事例に転用して
高値で売りつけるようになってきた..気がする。

機械学習を少し勉強した人は単なる「転移学習(Tranfer Learning)」の流用に過ぎないが
VGG16やのInseptionV3やらの性能の良い学習済み画像分類器なんかが
ライブラリに入っているものだから..

..と、話がそれていきそうなのでやめにするが...

ようはこれから至近何ヶ月かは
Canned Estimator の時代
が来ていることを個人的に実感している。


転移学習は基本、
少ない学習データで学習済みモデルを追加学習させる方法
なのだけど、
これをやるには
学習済みモデルのお尻の層数層+新規の層数層を加えて
少ない学習データをバッチ実行させて追加学習させ無くてはならない。

Keras の VGG16 とかの既存学習モデルは
はなっからそう使われるだろうなと想定されていて
簡単に転移学習できるようになっているが
TensorFlowのEstimatorは
Estimator に合う入力データ、バッチデータにしてやらないといけない。

でもTensorFlowというライブラリは
バリバリのモデル自体の開発者向けという面をもっているので
入力データを input_fn という一定のルールで書かれた関数にしてやらないと
食わせることができない、いっちょかみできないのだ。


で、いろいろ調べているのだけど 1.4.0 が出たばっかりなので
本家サイト以外にさっぱり情報がない..

で、本家サイトを読んでいたらこのリンクを見つけた。

Introducing tf.data
Derek Murray
https://docs.google.com/presentation/d/16kHNtQslt-yuJ3w8GIx-eEH6t_AvFeQOchqGRFpAD7U/edit?usp=sharing



と、いうことで早速勝手に翻訳してみた。
ちょうどスライドのノート部分にDerek Murray氏の喋った内容も記載されているので
一緒に翻訳してみた。

以下その翻訳文だが、当然間違っていても一切責任はとりません。
参照する際は、at your own riskでお願いします。

-------



 皆さん、私はDerek Murrayです。今日、私は入力パイプラインについてお話します。

このセッションでは、入力データ用の効率的で複雑なパイプラインを簡単に定義できる入力パイプライン用の新しいライブラリについて説明します。

ライブラリでは tf.data と呼ばれ、TensorFlow 1.2がリリースされた 5月に初めて登場しました...

...そして先日の1.4 リリースにて、TensorFlowをコアに無事移行になったことがアナウンスされました。



私はなぜ入力パイプラインについて話しているのしょう?
それは、入力データは機械学習の生命線だからです。
私たちはモデルを高精度に訓練するために巨大なデータセットに効率的にアクセスすることに頼りっきりになっているのです。







 しかし、現在のアクセラレータ(GPU)は非常に枯渇しています。 NVIDIA の Volta や Cloud TPU のようなアーキテクチャは、数年前よりもはるかに高速であり、入力パイプラインから非常に高いスループットを要求して飽和状態を維持します。



 率直に言うと、GPU枯渇はパフォーマンスの問題だけではありません...あなたのデータをTensorFlowに簡単に取り込む方法が必要です。
以前は、基本的に2つのオプションがありました...




 ひとつは、feed_dict メカニズムです。

この方法では、すべての入力処理をTensorFlow 計算グラフ外の Pythonプログラム上に置いています。

これを行うことの良い点は、Pythonのもつ柔軟性を享受でき、任意のデータ形式での作業が容易になることです。

しかし、この方法ではパフォーマンスは悪くなることがあります。利用者はしばしば単一のスレッドで入力データを処理することとなり、それがクリティカルパスとなっています。その間アクセラレータは全くの役立たずとなるのです。







..また、2つめの選択肢として、あなたのプロセスを TensorFlow の C++ オペレーションに移して、それらを TensorFlow の "生産者/消費者" キューを使うために文字列化するという方法もあります。

このスライド上のこのようなAPIは、ちょっとした待ち行列を構築し、パイプラインを与え、並列性を得ることで、データを placeholder へコピーする時間の合計を削減させています。

TensorFlowプログラムで ”キューランナーの開始(start queue runnners)" を聞いたことがありますか?これは、待ち行列の間で要素を移動する小さなグラフを実行するためにPythonスレッドを分岐させる神秘的なライブラリコールのことです。(start queu runnerを)呼び出すことを忘れても、複雑な並列 Python プログラムを実行してキューをいっぱいにしておく必要があります。グローバルインタープリタロックは、1秒あたりに処理できるレコード数を厳しく制限します。

コンセプト上の欠点もあります。--いい意味で--APIがこれらのスレッドの詳細を隠蔽しようとするため、キューベースのパイプラインはすべての入力データに対して単一のグローバルな1回限りのパイプラインになり、実行時に入力ソースを変更することはほとんど不可能になります。

このため、これらよりも高速で、キューよりもはるかに使いやすい代わりになるAPIを設計したいと考えたのです。



 では、なぜ我々が、関数入力パイプラインのための新しいTensorFlow APIである tf.data のために数ヶ月を費やしたのかについて話したいと思います。

ここでは、みなさんに APIのツアーを提供して、あなたが何をすることができるかを示していきたいと思います。

私たちがtf.data を設計していたときは、機能プログラミングの分野からも重要な洞察が得ていました。


入力パイプラインは、関数型言語における"のろまなリスト(遅延リスト:lazy list)"のようなものなのです。

何故我々がそう設計したと思いますか?



 データ要素はほとんど同質であるため、(Pythonの)listのようなものと同じです。フィーチャは異なるshapeを持つこともありますが、一般的には同じタイプのフィーチャを持ちます。


データセット全体が大きすぎてすべてを一度に実現できない場合や、データを自分で生成している場合は無限になる可能性があります。

そこれ、それらをlazy(=のろま)にすることを考えました。


 一旦のろまなリストのようなそれらについて考え始めると、どうして map() filter() のような高次関数を作成するのかがわかるとおもいます。



 これは決して新しい考えではなく、実際にはC# の LINQ 、Scala のコレクション、 Java8 のストリームなどの主流言語の標準ライブラリの一部にもなっています。 そして、まったく正直なところ、斬新さの欠如はここでは良いことです。それが定石としてうまくいくことはわかっていますし、Stream Fusion、Shortcut Deforestation、そしてもしかすると、パフォーマンスを向上させるためのSQLクエリの最適化などで--あなたも書いたことのあるようなプログラム最適化に関する膨大な文献があります。

したがって、入力パイプラインを機能的なプログラムとして記述することができれば、良い形になります...



 ...これが、関数入力パイプライン用の新しいTensorFlow APIであるtf.dataを作成した理由です。


 tf.data には、TensorFlowプログラムへの2つの新しいインターフェイスが導入されています。

1つは、要素のコレクションを表す Dataset インターフェイスです。関数型のアナロジーが好きだった場合-- Dataset はテンソルのタプルののろまなリストと考えることができます。



 それらをどのようにして作成するかについての幾つかのデータセットの例を紹介します。
最初のデータセットの例は "ソース" で、1つ以上のテンソルオブジェクトから作成しています。



 たとえば、最も簡単なソースは Dataset.from_tensors() で、これはテンソルのタプルから単一要素のデータセットを作成しています。



 これらのテンソルを複数の要素に分割したい場合は、 "from_tensor_slices"を使用します。


 もしデータをディスクから取得するのであれば、TextLineDatasetなどのファイルリストを読み取るファイル形式パーサを使用して、これらのファイル内の各行に対して1つの文字列要素を含むデータセットを生成することができます。



 第2の選択肢としては、機能変換を使用して1つのデータセットを別のデータセットに変換することです。 一覧でおみせするにはあまりにも多くの変換があるのですが、より一般的なもののいくつかは以下を含みます...


...map(), ここではデータセットの各要素へ関数を適用します...





... repeat() を使用して、入力データセットを複数回ループすることができます...

...そしてバッチ、ここでは元のデータセットから複数の連続した要素をまとめてバッチを作成します。

 そして、APIには数多くの基本的な変換があります(主に標準的な高次関数のリストに基づいており、それらを組み合わせてより複雑なものにすることができるように設計されています)。


 この良いサンプルとしては、TensorFlow 1.4に追加した py_func opといくつかの標準データセットから作成した新しい Dataset.from_generator() のソースです。

このソースを使用すると、入力処理ロジックをPythonジェネレータとして記述することができます。これをデータセットに変換して、他の変換と一緒に作成することができます。 これはユーザの生産性を変えるものであり、数週間しか利用できないにもかかわらず、 feed に慣れている方がより良い成果を望む場合はより簡単になると思います。





 さあ、それをまとめてみましょう。 ファイル名のリストから始めて、TFRecordDatasetを使ってレコードをバイナリ blob として取得し、構文解析関数をマップしてテンソルに変換し、ランダムシャッフルし、100回のトレーニングを繰り返し、最後に128個の連続要素のバッチを連結し単一の要素に変換します。

多くの人々の最初のデータセットパイプラインはこのように見えます。シャッフルリピートバッチは、ミニバッチSGDを実行するときの共通のモチーフです。

データセットのパイプラインを読んで、 feed やキューベースのプログラムを見ているよりも、何が起こっているのかを簡単に伝えることができていたらうれしいのですが。







パイプラインをデータセットとして定義してきました。

あなたはどの方法でモデルをトレーニングするためにテンソルを取得しましたか?

2つめの補足は Iterator インターフェイスです。Iterator は他のプログラミング言語のイテレータとよく似ています。Iterator はデータセット内の現在の位置を維持し、テンソルのタプルとして次の要素にアクセスする方法を提供します。




iteratorメソッドを呼び出すことによってデータセットからIteratorを作ることができます。ここにはいくつかのオプションがあります。



単純なケースでは、1回のみのイテレータがうまく機能します。自動的に初期化され、データを1回通過させることができます。これは、基本的にキューから取得できる機能を提供します。



より高度なオプションは、初期化可能なイテレータで、複数のデータソース間の切り替えなど、より洗練された使い方を提供しています...


..主な違いは、初期化可能なイテレータは、初期化のために実行できるオペレーションを与えることができます。

・複数回実行可能

・異なるファイルリストや異なるエポック数などのようなパラメータを実行時にfeed可能です。




イテレータを設定したら、 get_next() メソッドを使用してイテレータから次の要素にアクセスできます。 これは古典的な TensorFlow で、遅延実行があるため、session.run に渡して次の要素を生成する必要があります。



実際にどうやって使うのでしょうか?

ここで、画像とラベルのバッチのデータセットがあるとします。
イテレータを作成し、get_nextの結果をあなたのモデルと最適化機能を使って訓練を行い、その訓練をループで実行します。

OutOfRangeErrorは、ファイルの終わりに対する私たちの魅力的な名前です。

この定型文をすべてあなた自身で書く必要はないことに注意してください...




たとえば、Estimator APIを使用している場合は、最初の数行を入力関数にラップするだけで、Estimatorがループ処理を行います。



キューではできないこともあなたに味わっていただきたいと思います。

(1エポックと呼んでいる単位の)データを正確に一通り実行し、次を開始する前に幾つかの エポック終了時の計算(end-of-epoch computation) を実行するとしましょう。

古いキューベースのAPIでは、基本的に単一のグローバルなone-shot(1回だけの) iterator があるため、これをやりたい場合はsessionを解体して再起動する必要があります。これは不器用で処理も遅くなります。

このスライドでは、初期化可能なイテレータがその問題をどのように解決するかを示しています。このように、エポックを越えて外側のループを持つことができます。そのループの始めにイテレータを初期化し、範囲外になるまで訓練し、最後にエポックの計算を行い、パイプラインを再起動します。

エポック終了時の計算では、検証のために別のデータセットを反復処理する必要があるかもしれません。あるいは、イニシャライザをパラメータ化して、各エポックで異なる処理を行うこともできます。





tf.data APIをまとめましょう。
ここでは、学ぶべき2つの新しいクラスがありました。
tf.data.Dataset では、データソースと機能変換の構成としての入力パイプラインを表しました。

また、tf.data.Iteratorを使用すると、データセットから要素にシーケンシャル(順番)にアクセスすることができました。

うまくいけば、既存のAPIよりシンプルであると確信しています。


tf.data の柔軟性は次の2つの点からもたらされます:

・複雑なパイプラインを構築するために一緒に構成された豊富な変換セット



・同じプログラム内に複数のデータセットとイテレータを作成できるため、振る舞いをパラメータ化してさまざまなソース間を切り替えることが可能

なことです。




パフォーマンスについても少し語りたいとおもいます。

ここでの努力に対する全ての目的は、使いやすく高速なものを作ることにあります。




あなたがこれまでずっと feed_dict ベースのプログラミングをしてきているのであれば、かなりのスピードアップを期待できるでしょう。

実装されている内容を詳しく説明する時間はありませんが、主な性能向上ポイントとしては、Pythonのオーバーヘッドを避けるために C++ で tf.data を実装しているところにあります。

tf.data パイプラインを同等のキューベースのパイプラインと比較すると、同様の構造のキューとスレッドが使用されますが、クリティカルパスには Python キューランナースレッドが存在しないため、tf.data パイプラインは グローバルインタープリタロックによって制限され、より高いスループットにエンハンスされます。





ただし、現在の実装は、デフォルトでは決定論的(確定的)、順次および同期的です。 すべてのステージのバッファとスレッドを作成し、要素を生成するために競合するキューベースのAPIとは異なり、並列性と非決定性のオプトインを行うという控えめなアプローチをとってきました。 その理由の1つは、トレーニングモデルのRAM要件が非常に大きくなる可能性があるということです。1つのバッチはマルチGPUイメージモデルでは数百MBから数ギガバイトになる可能性があり、コンピュータのスラッシングを開始するよりも遅く予測可能です。

幸いにも、我々はより高いパフォーマンスの選択を容易にしました...






このデータセットのサンプルを見て、パフォーマンス関連のコードを入れようとしている不審なスペースをつかって再編成してみしましょう!

私は努力順で行くつもりです...




まず、非同期パイプラインを有効にし、dataset.prefetch() でこれを行うことができます。これは本質的に恒等変換ですが、バックグラウンドスレッドとバインドされたバッファを作成して要素をプリフェッチします。

残りのパイプラインのコストが安い場合は、現在のバッチのトレーニングと次のバッチの事前処理を重複させることができます。




事前処理がより大変な場合は、並列化したくなるかもしれません。
これは、 num_parallel_calls を dataset.map() に追加するだけで簡単にできます。

今度は、TensorFlowスレッドプールを使用して一度に64レコードを解析します。




最後の方法は、GCSのような分散ファイルシステムを使ってデータを保存する場合に最も便利です。

ファイルベースのデータセットでは、一度に1つのファイルが読み込まれるため、ほとんどの時間を I/O のブロックに費やす可能性があります。

この小さなインターリーブとプリフェッチガジェットを使用すると、8つのファイルを同時に読み込み、バッファを保持してレイテンシの変動を隠すことができます。




さて、ここまで新しい tf.data API を紹介してきました。

セッションの終盤は、今後何が起こっていくのかを知りたいだけだとおもいます。

私はすでに、TensorFlow 1.4 において API が tf.dataに移動していると述べました。
これのバージョンから、後方互換性の保証をしていきます。みなさんのプロダクトではそれに頼ることをお勧めします。




我々の次の大きな目標は、GPUメモリへの自動ステージングを可能にすることです。 これはパフォーマンスにとって重要です。プログラムの1行の変更でこれを有効にする必要があります。



そして長期的には、自動パフォーマンス最適化のチャンスに興味があります。 私は既に静的最適化に関する豊富な文献があると述べましたが、新しいAPIは、実行時にバッファサイズ、スレッド数、プレースメントを設定する興味深い可能性を与えてくれますし、これらを最適化するLearning2Learnや強化学習のテクニックのような事さえ使うでしょう。



このセッションでは、TensorFlow内へのデータ取得は苦痛を伴うものではないことを示しました。

tf.data はシンプルですが、高速で、柔軟性があり、以前はできなかったあらゆることを行うことができます。

あなたがそれを使って構築することを聞くのが待ちきれません。

ああ。 また、私たちが公開したばかりの基礎を紹介するブログポストもあります...

聞いてくれてありがとう、質問をいただけると嬉しいのですが..




-------


..多分ノート部分は、
Youtubeの自動テロップ機能の出ててきた英語をそのままではなく
直しているのだとおもうのだけど、
ところどころ..ああ、ここ、ドヤ顔してるな..きっと..というのが
よめる英語だった..

実装のスードコードが載っていたので
非常によくわかった。

キューっていうモノがあるのは
本家サイトを翻訳していたときから知ってはいたんだけど、
(もちろん非同期の基本モデルの登場人物だってことも)
これまでは「まずは動作させること」に命を燃やしていたので
feed_dict 経由一本槍だった..


..たしかに feed_dict で投入する段階って、
TensorFlow の C++ の実装との境界レベルだともうので
そこまでずーっとシングルスレッド一本で書いてたら
遅くなるよなあ..



..入力層にデータを入れるところからが
機械学習と思っているほかのライブラリと違って、
TensorFlowは
データ入力するところ(事前処理)も機械学習ですって
いっているわけね。

バッチ毎回の豆乳までにその分のデータを用意すりゃいいでしょ
っていうことで遅延リストを使うあたり
DBアクセスのHibernateっぽい..

やっぱコンピュータは I/O がボトルネックになるのは
基本中の基本ということか..

にしても.. バッチ1回毎に学習データに変化を持たせる..
..域まで機械学習こなしてないなあ..

..最先端の機械学習屋はそんなこともやってるんだ..




p.s.

そうか..

これからしばらくは、
Qiitaとかに出してるTensorFlowサンプルコード
人の書いたコードとかの
session を withしてるなかで
feed_dictをつかっていたら
まだまだ、わかってないね、チミ(シー、シー)
とか言えば、
TensorFlowなんちゃって玄人
にすぐなれるってことか..


..もちろん、
この記事を読んでいる人は
みないい人たちだから
そんなことしませんよね...

2017年10月23日月曜日

Proxy環境下でHubotコンテナが起動しなくなった

前に書いた記事

HuBotを使ってRocketChat用のチャットボットを試しに作ってみる
https://fight-tsk.blogspot.jp/2017/04/hubotrocketchat.html

で書いたスクリプトがProxy環境下で動作しなくなってしまった
どうも

[GitHub] hubot-rocketchat
https://github.com/RocketChat/hubot-rocketchat

をcloneしてちくちくbuildしていくと
npmコマンドへのproxyを設定しないとビルドに失敗してしまうのだ。

Dockerfileの中でスイッチユーザしてたりして厄介だ..

..ということで、Dockerfileを自分で書いてみた。

ベースイメージはnodeの安定版がv6.11らしいので、コッチをつかってみた。


FROM node:6.11
MAINTAINER hara hara development

# change your setting or overwrite with build parameters
ARG HTTP_PROXY=http://proxy.server:8080
ARG HTTPS_PROXY=$HTTP_PROXY

USER root
RUN npm -g config set proxy $HTTP_PROXY && \
    npm -g config set https-proxy $HTTPS_PROXY && \
    npm -g config set registry http://registry.npmjs.org/ && \
    npm install -g yo generator-hubot && \
    npm install  -g hubot-rocketchat@1 && \
    useradd hubot -m
USER hubot
WORKDIR /home/hubot

# change your setting or overwrite command parameter
ENV ROCKETCHAT_URL=change_rocketchat_server:3000
ENV ROCKETCHAT_ROOM=
ENV BOT_OWNER="Change Your Name "
ENV BOT_NAME=bot
ENV BOT_DESC="change bot description"
ENV ROCKETCHAT_USER=bot
ENV ROCKETCHAT_PASSWORD=password
ENV ROCKETCHAT_AUTH=password
ENV LISTEN_ON_ALL_PUBLIC=true
ENV EXTERNAL_SCRIPTS=hubot-diagnostics,hubot-help,hubot-google-images,hubot-google-translate,hubot-pugme,hubot-maps,hubot-redis-brain,hubot-rules,hubot-shipit,hubot-jenkins-notifier,hubot-grafana
ENV TZ=Asia/Tokyo

RUN cd $HOME && \
    yo hubot --owner=$BOT_OWNER --name=$BOT_NAME --description=$BOT_DESC --adapter="rocketchat@1" && \
    node -e "console.log(JSON.stringify('$EXTERNAL_SCRIPTS'.split(',')))" > external-scripts.json && \
    npm install $(node -e "console.log('$EXTERNAL_SCRIPTS'.split(',').join(' '))")

EXPOSE 8080

CMD bin/hubot $BOT_NAME -a rocketchat



DockerfileENVARG指定を、各自の環境に合わせて変更して、

mkdir -p data && chmod a+rwx data && sudo docker build --name myhubot:latest .
sudo docker run -v data:/home/hubot/scripts -p3001:8080 mybot:latest

を実行してもいいし、

mkdir -p data && chmod a+rwx data
sudo docker build --name hara2/myhubot:latest --build-arg HTTP_PROXY=http://my,proxy:8080 --build-arg HTTPS_PROXY=http://my.proxy.8080 .
sudo docker run -e ROCKETCHAT_USER=mybot -e ROCKETCHAT_PASSWORD=mybotpass -v data:/home/hubot/scripts -p 3001:8080 hara2/myhubot:latest


と全部引数指定しても良い。

hubot-scripts.jsonexternal-scripts.jsonの扱いが今後も変わってきそうなので
このあたりまた動かかなくなるかもしれない。

けどDockerfileを見てもらったらわかると思うけど、
インストール手順に従っただけなので、
動かなくなったらちょこちょこ直して動かせるようにするにはそう難しい技術がいるわけでもない。

#ので、今後動かなくなっても、あとは各自でやってください..

Google Colab相当の環境をローカルPC上のDocker Composeで実現する

 Docker Composeの有料版に、お金を払いたくない ^H^Hが使えない ため古いゲーミングPC(GeForce3080)にDocker Composeを入れてその上のコンテナでGoogle Colab相当のノート操作ができないかためしてみた: コンテナイメージとして a...