はい、純粋なSQLクエリステートメントでニューラルネットワークを実装できます。

はい、純粋なSQLクエリステートメントでニューラルネットワークを実装できます。

[[229220]]

よく知られているように、SQL は、開発者が大量のデータに対して効率的な操作を実行できるようにするデータベース クエリ ステートメントです。しかし、この記事では別の観点から SQL クエリ ステートメントをネストし、単純な 3 層の全接続ネットワークを構築します。ステートメントのネストが深いため効率的に計算することはできませんが、それでも非常に興味深い実験です。

この記事では、1 つの隠し層 (ReLU および softmax 活性化関数を使用) を持つニューラル ネットワークを純粋に SQL で実装します。フォワードプロパゲーションとバックプロパゲーションを含むこれらのニューラル ネットワーク トレーニング手順は、BigQuery の単一の SQL クエリで実装されます。 BigQuery で実行すると、実際にはニューラル ネットワークのトレーニングが数百または数千のサーバーに分散されます。素晴らしいですね。

そうは言っても、これは宣言型データの観点からニューラル ネットワークのトレーニングを検討しながら、SQL と BigQuery の限界をテストする興味深いプロジェクトです。このプロジェクトでは実用的なアプリケーションは考慮されていませんが、後ほど実用的な研究の意味合いについて説明します。

まず、ニューラル ネットワークに基づく単純な分類器から始めます。入力サイズは 2 で、出力はバイナリ分類です。次元 2 の単一の隠し層と ReLU 活性化関数を使用します。出力層のバイナリ分類では、softmax 関数が使用されます。ネットワークを実装する際の手順は、Karpathy の CS231n チュートリアルで紹介されている Python の例の SQL ベースのバージョンになります。

モデル

モデルには次のパラメータがあります。

隠れ層への入力

  • W: 2×2 重み行列 (要素: w_00、w_01、w_10、w_11)
  • B: 2×1バイアスベクトル(要素: b_0、b_1)

出力層に隠されている

  • W2: 2×2 重み行列 (要素: w2_00、w2_01、w2_10、w2_11)
  • B2: 2×1バイアスベクトル(要素: b2_0、b2_1)

トレーニング データは、次の入力列 x1 と出力列 x2 を持つ BigQuery テーブルに保存されます (テーブル名: example_project.example_dataset.example_table)

前述したように、トレーニング全体を単一の SQL クエリとして実装します。トレーニングが完了すると、パラメータ値が SQL クエリ ステートメントを通じて返されます。ご想像のとおり、これはネストされたクエリであり、このクエリを準備するために段階的に構築していきます。最も内側のサブクエリから開始し、外側のレベルを 1 つずつネストします。

前方伝播

まず、重みパラメータ W と W2 を正規分布に従うランダムな値に設定し、重みパラメータ B と B2 を 0 に設定します。 WとW2のランダム値はSQL自体で生成できます。簡単にするために、これらの値を外部で生成し、SQL クエリで使用します。パラメータを初期化するために使用される内部サブクエリは次のとおりです。

  1. *を選択  
  2. -0.00569693 AS w_00、
  3. 0.00186517 AS w_01、
  4. 0.00414431 AS w_10、
  5. 0.0105101 AS w_11、
  6. 0.0 AS b_0、
  7. 0.0 AS b_1、
  8. -0.01312284 AS w2_00、
  9. - 0.01269512 AS w2_01、
  10. 0.00379152 AS w2_10、
  11. -0.01218354 AS w2_11、
  12. 0.0 AS b2_0、
  13. 0.0 AS b2_1
  14. `example_project.example_dataset.example_table`から

テーブル example_project.example_dataset.example_table にはすでに列 x1、x2、y が含まれていることに注意してください。モデル パラメータは、上記のクエリ結果に追加の列として追加されます。

次に、隠れ層の活性化値を計算します。要素 d0 と d1 を含むベクトル D を使用して、隠し層を表します。行列演算 D = np.maximum(0, np.dot(X, W) + B) を実行する必要があります。ここで、X は入力ベクトル (要素 x1 と x2) を表します。この行列演算は、重み W を入力 X で乗算し、バイアス ベクトル B を加算することで構成されます。結果は非線形 ReLU 活性化関数に渡され、負の値は 0 に設定されます。 SQL での同等のクエリは次のようになります。

  1. *を選択
  2. 場合 
  3. ((x1*w_00 + x2*w_10) + b_0) > 0.0 のとき ((x1*w_00 + x2*w_10) + b_0)
  4. それ以外の場合0.0
  5. END ) d0として
  6. 場合 
  7. ((x1*w_01 + x2*w_11) + b_0) > 0.0 のとき ((x1*w_01 + x2*w_11) + b_1)
  8. それ以外の場合0.0
  9. 終了) AS d1
  10. FROM {内部サブクエリ }

上記のクエリは、前の内部サブクエリの結果に 2 つの新しい列 d0 と d1 を追加します。 上記のクエリの出力は以下に表示されます。

これにより、入力層から隠し層への遷移が完了します。これで、隠し層から出力層への変換を実行できます。

まず、出力層の値を計算します。式は、スコア = np.dot(D, W2) + B2 です。次に、計算された値に対してソフトマックス関数を使用して、各クラスの予測確率を取得します。 SQL での同等のサブクエリは次のとおりです。

  1. *を選択
  2. EXP(スコア0)/(EXP(スコア0) + EXP(スコア1)) AS probs_0、
  3. EXP(スコア1)/(EXP(スコア0) + EXP(スコア1)) AS probs_1
  4. から   
  5. (選択*,
  6. ((d0*w2_00 + d1*w2_10) + b2_0) ASスコア_0、
  7. ((d0*w2_01 + d1*w2_11) + b2_1) ASスコア_1
  8. FROM { INNERサブクエリ })

まず、クロスエントロピー損失関数を使用して、現在の予測の合計損失を計算します。まず、各例の正しいクラスの予測確率の対数の負の値が計算されます。クロスエントロピー損失は、これらの X インスタンスと Y インスタンス全体の値の平均です。自然対数は増加関数なので、損失関数を正しいクラスの予測確率の負の対数として定義するのが直感的です。正しいクラスの予測確率が高い場合、損失関数は低くなります。逆に、正しいクラスの予測確率が低い場合、損失関数の値は高くなります。

過剰適合のリスクを減らすために、L2 正則化も追加します。全体的な損失関数には、0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W2*W2) が含まれます。ここで、reg はハイパーパラメータです。この関数を損失関数に含めると、重みベクトルの大きな値にペナルティが課せられます。

クエリでは、トレーニング例の数 (num_examples) も計算します。これは後で平均を計算するときに役立ちます。 SQL クエリで全体の損失関数を計算するステートメントは次のとおりです。

  1. *を選択
  2. (sum_correct_logprobs/num_examples) + 1e-3*(0.5*(w_00*w_00 + w_01*w_01 + w_10*w_10 + w_11*w_11) + 0.5*(w2_00*w2_00 + w2_01*w2_01 + w2_10*w2_10 + w2_11*w2_11)) AS損失
  3. から   
  4. (選択*,
  5. SUM (correct_logprobs) OVER () sum_correct_logprobs、
  6. COUNT (1) OVER () num_examples
  7. から    
  8. (選択*,
  9. 場合 
  10. y = 0 のとき-1 *LOG(probs_0)
  11. それ以外の場合-1*LOG(probs_1)
  12. END ) AS correct_logprobs
  13. FROM {内部サブクエリ}))

バックプロパゲーション

次に、バックプロパゲーションでは、損失関数に対する各パラメータの偏微分を計算します。最初の層から次の層まで計算するには、連鎖律を使用します。まず、クロスエントロピーとソフトマックス関数の導関数を使用してスコアの勾配を計算します。対応するクエリは次のとおりです。

  1. *を選択
  2. 場合 
  3. y = 0 のときは( probs_0–1)/num_examples、そうでなければprobs_0/num_examples
  4. END ) AS dscores_0、
  5. 場合 
  6. y = 1 のとき( probs_1–1)/num_examplesそれ以外の場合probs_1/num_examples
  7. END ) AS dscores_1  
  8. FROM {内部サブクエリ }

上記では、scores = np.dot(D, W2) + B2 を使用してスコアを計算しました。したがって、スコアの偏微分に基づいて、隠れ層 D の勾配とパラメーター W2、B2 を計算できます。対応するクエリステートメントは次のとおりです。

  1. *を選択
  2. 合計(d0*dscores_0) OVER ()を dw2_00として計算します
  3. 合計(d0*dscores_1) OVER () をdw2_01として計算します  
  4. 合計(d1*dscores_0) OVER () をdw2_10として計算します  
  5. 合計(d1*dscores_1) OVER () をdw2_11として計算します  
  6. 合計(dscores_0) OVER ()としてdb2_0、  
  7. 合計(dscores_1) OVER () AS db2_1、  
  8. 場合   
  9. ( d0 ) <= 0.0 のとき0.0  
  10. それ以外の場合(dscores_0*w2_00 + dscores_1*w2_01)  
  11. 終わり  AS dhidden_​​0、  
  12. 場合   
  13. (d1 ) <= 0.0 のとき0.0  
  14. それ以外の場合(dscores_0*w2_10 + dscores_1*w2_11)  
  15. 終わり  AS dhidden_​​1  
  16. FROM {内部サブクエリ }

同様に、D = np.maximum(0, np.dot(X, W) + B) であることが分かります。したがって、D の偏微分を取ることで、W と B の微分を計算できます。 X の偏微分はモデルのパラメータではなく、他のモデルパラメータを介して計算する必要がないため、計算する必要はありません。 W と B の偏微分を計算するためのクエリ ステートメントは次のとおりです。

  1. *を選択  
  2. 合計(x1*dhidden_​​0) OVER () AS dw_00、  
  3. 合計(x1*dhidden_​​1) OVER () AS dw_01、  
  4. 合計(x2*dhidden_​​0) OVER () AS dw_10、  
  5. 合計(x2*dhidden_​​1) OVER () AS dw_11、  
  6. SUM (dhidden_​​0) OVER () AS db_0、  
  7. 合計(dhidden_​​1) OVER () AS db_1  
  8. FROM {内部サブクエリ }

***、更新演算にはそれぞれ W、B、W2、B2 の導関数を使用します。計算式は param = learning_rate * d_param です。ここで、learning_rate はパラメーターです。 L2 正則化を反映するために、dW と dW2 を計算するときに、正規項 reg*weight を追加します。また、トレーニング データ (x1、x2、y 列) とモデル パラメーター (重みとバイアス項) を格納するためにサブクエリ中に作成された dw_00、correct_logprobs などのキャッシュされた列も削除します。対応するクエリステートメントは次のとおりです。

  1. 選択x1、
  2.   x2、
  3. はい、  
  4. w_00 — (2.0)*(dw_00+(1e-3)*w_00) w_00として  
  5. w_01 — (2.0)*(dw_01+(1e-3)*w_01) w_01として  
  6. w_10 — (2.0)*(dw_10+(1e-3)*w_10) w_10として  
  7. w_11 — (2.0)*(dw_11+(1e-3)*w_11) w_11として  
  8. b_0 — (2.0)*db_0 は b_0であり  
  9. b_1 — (2.0)*db_1 が b_1の場合  
  10. w2_00 — (2.0)*(dw2_00+(1e-3)*w2_00) w2_00として  
  11. w2_01 — (2.0)*(dw2_01+(1e-3)*w2_01) w2_01として  
  12. w2_10 — (2.0)*(dw2_10+(1e-3)*w2_10) w2_10として  
  13. w2_11 — (2.0)*(dw2_11+(1e-3)*w2_11) w2_11として  
  14. b2_0 — (2.0)*db2_0 は b2_0として  
  15. b2_1 — (2.0)*db2_1 b2_1として 
  16. FROM {内部サブクエリ }

これには、前方伝播と後方伝播の完全な反復プロセスが含まれます。上記のクエリは更新された重みとバイアスを返します。結果の一部を以下に示します。

複数のトレーニング反復を実行するには、上記のプロセスを繰り返します。これを行うには、単純な Python 関数で十分です。コード リンクは次のとおりです: https://github.com/harisankarh/nn-sql-bq/blob/master/training.py。

反復回数が多すぎるため、クエリ ステートメントが著しくネストされています。 10 回のトレーニング反復を実行するためのクエリ ステートメント アドレスは次のとおりです。

https://github.com/harisankarh/nn-sql-bq/blob/master/out.txt

クエリ ステートメントの複数のネストと複雑さにより、BigQuery でクエリを実行すると複数のシステム リソースが使い果たされます。 BigQuery の標準 SQL 拡張機能は、従来の SQL 言語よりも拡張性に優れています。標準の SQL クエリであっても、10 万インスタンスのデータセットに対して 10 回以上の反復を実行するのは困難です。リソースの制約により、モデルを評価する際には単純な決定境界を使用し、少数の反復で良好な精度が得られるようにします。

入力 X1、X2 が標準正規分布に従う単純なデータセットを使用します。バイナリ出力 y は、x1 + x2 が 0 より大きいかどうかを単純にチェックします。 10 回のトレーニングの反復をより速く完了するために、より大きな学習率 2.0 を使用します (注: このような大きな学習率は実際の使用には推奨されず、発散を引き起こす可能性があります)。上記のステートメントを 10 回繰り返して実行することによって取得されるモデル パラメーターは次のとおりです。

Bigquery の save to table 関数を使用して、結果を新しいテーブルに保存します。これで、トレーニング セットに対して推論を実行し、予測値と期待値を比較できるようになりました。クエリ スニペットは次のリンクにあります。

https://github.com/harisankarh/nn-sql-bq/blob/master/query_for_prediction.sql を参照してください。

わずか 10 エポックで、精度は 93% になりました (テスト セットでも同様です)。

反復回数を 100 に増やすと、精度は 99% に達します。

最適化

以下はこのプロジェクトの概要です。私たちはこれからどんなインスピレーションを得るのでしょうか?ご覧のとおり、リソースのボトルネックによってデータセットのサイズと実行される反復回数が決まります。 Google にリソース制限を解放するよう祈るだけでなく、この問題を解決するための次の最適化方法もあります。

中間テーブルと複数の SQL ステートメントを作成すると、反復回数を増やすことができます。たとえば、最初の 10 回の反復の結果を中間テーブルに保存できます。次の 10 回の反復を実行するときに、同じクエリ ステートメントをこの中間テーブルに基づいて実行できます。したがって、20 回の反復を実行しました。このメソッドは、より大きなクエリの反復を処理するために繰り返し使用できます。

各ステップで外部クエリを追加する代わりに、ネストされた関数をできるだけ使用する必要があります。たとえば、2 レベルのネストされたクエリを使用する代わりに、1 つのサブクエリでスコアと確率を同時に計算できます。

上記の例では、最後の外部クエリが実行されるまで、すべての中間項目が保持されます。 correct_logprobs などの一部の項目は、以前に削除できた可能性があります (ただし、SQL エンジンはおそらくこのような最適化を自動的に実行します)。

ユーザー定義関数をより頻繁に適用するようにしてください。ご興味があれば、BigQuery のユーザー定義関数からモデルを提供するこのプロジェクトをご覧ください (ただし、トレーニングに SQL または UDF を使用することはできません)。

意義

それでは、ディープラーニング ベースの分散 SQL エンジンの根本的な意味について見てみましょう。 BigQuery や Presto などの SQL ウェアハウス エンジンの制限の 1 つは、クエリ操作が GPU ではなく CPU で実行されることです。 blazingdb や mapd などの GPU アクセラレーション データベース クエリの結果を研究するのは興味深いでしょう。シンプルな研究アプローチとしては、分散 SQL エンジンを使用してクエリとデータ分散を実行し、GPU アクセラレーション データベースを使用してローカル計算を実行することが挙げられます。

一歩引いて考えてみると、分散型ディープラーニングを実行するのは難しいことはすでにわかっています。分散 SQL エンジンは過去数十年にわたって広範囲にわたる研究が行われ、今日のクエリ計画、データ パーティション分割、操作配置、チェックポイント設定、マルチクエリ スケジューリングなどのテクノロジが生まれました。これらのいくつかは、分散ディープラーニングと組み合わせることができます。これに興味がある方は、分散データベースと分散ディープラーニングに関する広範な研究の議論が記載されているこの論文 (https://sigmodrecord.org/publications/sigmodRecord/1606/pdfs/04_vision_Wang.pdf) をご覧ください。

オリジナルリンク: https://towardsdatascience.com/deep-neural-network-implemented-in-pure-sql-over-bigquery-f3ed245814d3

<<:  人工知能搭載の携帯電話は私たちの生活をどのように変えるのでしょうか? 携帯電話メーカーが何をしてきたか見てみましょう。

>>:  数秒で理解:小売業における画像認識

ブログ    
ブログ    

推薦する

このオープンソースプロジェクトは、Pytorchを使用して17の強化学習アルゴリズムを実装しています。

強化学習は過去 10 年間で大きな進歩を遂げ、現在ではさまざまな分野で最も人気のあるテクノロジーの ...

ゲイツは間違っていた!これはロボットが仕事を奪うことに対処するための最善の解決策です

落ち着いてください。ロボットや人工知能 (AI) システムが人間の労働力を置き換えるにはまだ程遠いの...

2021 年のテクノロジートレンドはどこに向かうのでしょうか? IEEEが答えを教えます

[[357414]]この記事はLeiphone.comから転載したものです。転載する場合は、Leip...

ドローンによる食品配達が到来、こうした問題が注目を集めている

無人運転車による配達に続き、ドローンによる食品配達も現実化に向かって加速している。先日終了した202...

...

顔認識は安全ですか?どのような個人情報を慎重に保護すべきでしょうか?

デジタル化が進むにつれ、消費者は便利なインターネットサービスを体験できるようになり、携帯電話でタオバ...

GPT-2を使ってGPT-4を監督し、AIが人類を滅ぼすのを防ぐ?OpenAI Ilya Super Alignmentチームの最初の論文が発表される

たった今、OpenAI のチーフサイエンティスト Ilya が率いるスーパーアライメントチームが設立...

...

インテリジェントな運用とメンテナンスからスマートな運用まで、Qingchuang Technologyは企業に探偵シャーロックの能力を提供します

[51CTO.com からのオリジナル記事] 運用保守作業は、初期の手動運用保守から自動化運用保守、...

ファーウェイ、次世代スマート製品戦略と新+AIシリーズ製品を発表

[中国、上海、2019年9月18日] ファーウェイはHUAWEI CONNECT 2019で、Eng...

教師なし学習アルゴリズム: 異常検出

外れ値とは何でしょうか? Hawkins (1980) は外れ値の基本的な定義を与えました: 外れ値...

Googleは、生成AI製品のユーザーを著作権侵害の申し立てから保護することを約束

Googleは10月13日、現地時間公開のブログ投稿で、自社の生成AI製品のユーザーは当局によって保...

金メダルレベルの数学スキル:DeepMindの幾何学的推論モデルがNatureに掲載され、コードはオープンソースで、フィールズ賞受賞者が賞賛

今回、人工知能アルゴリズムが国際数学オリンピック(IMO)で大きな進歩を遂げました。本日発行された国...

なぜ誰もディープラーニングの本質を明らかにしないのでしょうか? !

[[213484]]人類はゆっくりと世界の本質に近づいています。物質は単に情報パターンの担い手にす...

...