はい、純粋な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

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

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

ブログ    
ブログ    

推薦する

ソラの13人のメンバーを解読:北京大学卒業生を含む中国人3人、博士号を取得したばかりの1人、そして21歳の天才

OpenAIはSoraで世界に衝撃を与えた。どのような才能あるチームがこのような傑作を開発できるので...

Metaはオープンソースのビッグモデルを緩和し、開発者が商用利用で利益を得られるよう検討していると報じられている。

6月16日、MetaのCEOマーク・ザッカーバーグ氏とその代理人は、Metaが開発中の新しい人工知...

AIの現実世界での最悪の使用例

人工知能(AI)の最悪のシナリオは、ハリウッドの大ヒット映画でおなじみのものだ。人間のような知性と知...

MLOpsの助けにより、AIは開発の黄金期を迎えることになる

12月21日、デロイトコンサルティングが最近発表したレポートでは、企業が一貫した機械学習運用(MLO...

PaddlePaddle をベースに構築された産業グレードの ICNET アプリケーションの予測速度は、TensorFlow を 20% 上回ります。

導入ICNET について話すとき、リアルタイム アプリケーションにおける画像セマンティック セグメン...

...

...

人工知能が教育改革にどのように貢献しているかをご覧ください

人工知能によってもたらされる将来の教育の変革と発展は、新たな機会を生み出すだけでなく、より大きな課題...

...

...

CityDreamer: ワンクリックで境界のない 3D 都市を生成

近年、3D自然シーンの生成に関する研究は盛んに行われていますが、3D都市の生成に関する研究はまだほと...

人工知能(AI)がビデオマーケティングを変える

ビデオ マーケティングで人工知能 (AI) を使用すると、企業はユーザーの好みを分析してカスタマイズ...

自動運転トラックの普及が加速しているが、実用化にはどれくらいの時間がかかるのだろうか。

iResearch Instituteが発表したレポートによると、2021年の中国の幹線物流大型ト...

...

本物と見間違えるほどリアルなAI変顔技術は本当に完璧なのか?

囲碁界の無敵の「アルファ碁」から、どこにでもある「顔認識」まで、機械学習は人々の生活に驚異的な変化を...