没错,纯SQL查询语句可以实现神经网络


我们熟知的SQL是一种数据库查询语句,它方便了开发者在大型数据中执行高效的操作。但本文从另一角度嵌套SQL查询语句而构建了一个简单的三层全连接网络,虽然由于语句的嵌套过深而不能高效计算,但仍然是一个非常有意思的实验。



在这篇文章中,我们将纯粹用SQL实现含有一个隐藏层(以及带 ReLU 和 softmax 激活函数)的神经网络。这些神经网络训练的步骤包含前向传播和反向传播,将在 BigQuery 的单个SQL查询语句中实现。当它在 BigQuery 中运行时,实际上我们正在成百上千台服务器上进行分布式神经网络训练。听上去很赞,对吧?


也就是说,这个有趣的项目用于测试 SQL 和 BigQuery 的限制,同时从声明性数据的角度看待神经网络训练。这个项目没有考虑任何的实际应用,不过最后我将讨论一些实际的研究意义。


我们先从一个基于神经网络的简单分类器开始。它的输入尺寸为 2,输出为二分类。我们将有一个维度为 2 的单隐层和 ReLU 激活函数。输出层的二分类将使用 softmax 函数。我们在实现网络时遵循的步骤将是在  Karpathy’s CS231n 指南(https://cs231n.github.io/neural-networks-case-study/)中展示的基于 SQL 版本的 Python 示例。


模型


该模型含有以下参数:


输入到隐藏层


  • 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)


训练数据存储在 BigQuery 表格当中,列 x1 和 x2 的输入和输出如下所示(表格名称:example_project.example_dataset.example_table)


如前所述,我们将整个训练作为单个 SQL 查询语句来实现。在训练完成后,通过 SQL 查询语句将会返回参数的值。正如你可能猜到的,这将是一个层层嵌套的查询,我们将逐步构建以准备这个查询语句。我们将会从最内层的子查询开始,然后逐个增加嵌套的外层。


前向传播


首先,我们将权重参数  W 和 W2 设为服从正态分布的随机值,将权重参数 B 和 B2 设置为 0。 W 和 W2 的随机值可以通过 SQL 本身产生。为了简单起见,我们将从外部生成这些值并在 SQL 查询中使用。用于初始化参数的内部子查询如下:


  1. SELECT *,

  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. FROM `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. SELECT *,

  2.       (CASE

  3.            WHEN ((x1*w_00 + x2*w_10) + b_0) > 0.0 THEN ((x1*w_00 + x2*w_10) + b_0)

  4.            ELSE 0.0

  5.        END) AS d0,

  6.       (CASE

  7.            WHEN ((x1*w_01 + x2*w_11) + b_0) > 0.0 THEN ((x1*w_01 + x2*w_11) + b_1)

  8.            ELSE 0.0

  9.        END) AS d1

  10. FROM {inner subquery}



上面的查询将两个新列 d0 和 d1 添加到之前内部子查询的结果当中。 上述查询的输出如下所示。



这完成了从输入层到隐藏层的一次转换。现在,我们可以执行从隐藏层到输出层的转换了。


首先,我们将计算输出层的值。公式是:scores = np.dot(D, W2) + B2。然后,我们将对计算出来的值用 softmax 函数来获得每个类的预测概率。SQL 内部的等价子查询如下:


  1. SELECT *,

  2.       EXP(scores_0)/(EXP(scores_0) + EXP(scores_1)) AS probs_0,

  3.       EXP(scores_1)/(EXP(scores_0) + EXP(scores_1)) AS probs_1

  4. FROM

  5.  ( SELECT *,

  6.           ((d0*w2_00 + d1*w2_10) + b2_0) AS scores_0,

  7.           ((d0*w2_01 + d1*w2_11) + b2_1) AS scores_1

  8.   FROM {INNER sub-query})



首先,我们将使用交叉熵损失函数来计算当前预测的总损失。首先,计算每个样本中正确类预测概率对数的负值。交叉熵损失只是这些 X 和 Y 实例中数值的平均值。自然对数是一个递增函数,因此,将损失函数定义为负的正确类预测概率对数很直观。如果正确类的预测概率很高,损失函数将会很低。相反,如果正确类的预测概率很低,则损失函数值将很高。


为了减少过拟合的风险,我们也将同样增加 L2 正则化。在整体损失函数中,我们将包含 0.5*reg*np.sum(W*W) + 0.5*reg*np.sum(W2*W2),其中 reg 是超参数。在损失函数中包括这一函数将会惩罚那些权重向量中较大的值。


在查询当中,我们同样会计算训练样本的数量(num_examples)。这对于后续我们计算平均值来说很有用。SQL 查询中计算整体损失函数的语句如下:


  1. SELECT *,

  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 loss

  3. FROM

  4.  (SELECT *,

  5.          SUM(correct_logprobs) OVER () sum_correct_logprobs,

  6.                                     COUNT(1) OVER () num_examples

  7.   FROM

  8.     (SELECT *,

  9.             (CASE

  10.                  WHEN y = 0 THEN -1*LOG(probs_0)

  11.                  ELSE -1*LOG(probs_1)

  12.              END) AS correct_logprobs

  13.      FROM {inner subquery}))


反向传播


接下来,对于反向传播,我们将计算每个参数对于损失函数的偏导数。我们使用链式法则从最后一层开始逐层计算。首先,我们将通过使用交叉熵和 softmax 函数的导数来计算 score 的梯度。与此相对的查询是:


  1. SELECT *,

  2.       (CASE

  3.            WHEN y = 0 THEN (probs_01)/num_examples

  4.            ELSE probs_0/num_examples

  5.        END) AS dscores_0,

  6.       (CASE

  7.            WHEN y = 1 THEN (probs_11)/num_examples

  8.            ELSE probs_1/num_examples

  9.        END) AS dscores_1

  10. FROM {inner subquery}



在上文中,我们用  scores = np.dot(D, W2) + B2 算出了分数。因此,基于分数的偏导数,我们可以计算隐藏层 D 和参数 W2,B2 的梯度。对应的查询语句是:


  1. SELECT *,

  2.       SUM(d0*dscores_0) OVER () AS dw2_00,

  3.       SUM(d0*dscores_1) OVER () AS dw2_01,

  4.       SUM(d1*dscores_0) OVER () AS dw2_10,

  5.       SUM(d1*dscores_1) OVER () AS dw2_11,

  6.       SUM(dscores_0) OVER () AS db2_0,

  7.       SUM(dscores_1) OVER () AS db2_1,

  8.      CASE

  9.          WHEN (d0) <= 0.0 THEN 0.0

  10.          ELSE (dscores_0*w2_00 + dscores_1*w2_01)

  11.      END AS dhidden_0,

  12.      CASE

  13.          WHEN (d1) <= 0.0 THEN 0.0

  14.          ELSE (dscores_0*w2_10 + dscores_1*w2_11)

  15.      END AS dhidden_1

  16. FROM {inner subquery}



同理,我们知道  D = np.maximum(0, np.dot(X, W) + B)。因此,通过 D 的偏导,我们可以计算出 W 和 B 的导数。我们无须计算 X 的偏导,因为它不是模型的参数,且也不必通过其它模型参数进行计算。计算 W 和 B 的偏导的查询语句如下:


  1. SELECT *,

  2.       SUM(x1*dhidden_0) OVER () AS dw_00,

  3.       SUM(x1*dhidden_1) OVER () AS dw_01,

  4.       SUM(x2*dhidden_0) OVER () AS dw_10,

  5.       SUM(x2*dhidden_1) OVER () AS dw_11,

  6.       SUM(dhidden_0) OVER () AS db_0,

  7.       SUM(dhidden_1) OVER () AS db_1

  8. FROM {inner subquery}



最后,我们使用 W、B、W2 及 B2 各自的导数进行更新操作。计算公式是 param = learning_rate * d_param ,其中learning_rate 是参数。为了体现 L2 正则化,我们会在计算 dW 和 dW2 时加入一个正则项 reg*weight。我们也去掉如  dw_00, correct_logprobs 等缓存的列,它们曾在子查询时被创建,用于保存训练数据(x1, x2 及 y 列) 和模型参数(权重和偏置项)。对应的查询语句如下:


  1. SELECT x1,

  2.       x2,

  3.       y,

  4.       w_00 (2.0)*(dw_00+(1e-3)*w_00) AS w_00,

  5.       w_01 (2.0)*(dw_01+(1e-3)*w_01) AS w_01,

  6.       w_10 (2.0)*(dw_10+(1e-3)*w_10) AS w_10,

  7.       w_11 (2.0)*(dw_11+(1e-3)*w_11) AS w_11,

  8.       b_0 (2.0)*db_0 AS b_0,

  9.       b_1 (2.0)*db_1 AS b_1,

  10.       w2_00 (2.0)*(dw2_00+(1e-3)*w2_00) AS w2_00,

  11.       w2_01 (2.0)*(dw2_01+(1e-3)*w2_01) AS w2_01,

  12.       w2_10 (2.0)*(dw2_10+(1e-3)*w2_10) AS w2_10,

  13.       w2_11 (2.0)*(dw2_11+(1e-3)*w2_11) AS w2_11,

  14.       b2_0 (2.0)*db2_0 AS b2_0,

  15.       b2_1 (2.0)*db2_1 AS b2_1

  16. FROM {inner subquery}



这包含了正向和反向传播的一整个迭代过程。以上查询语句将返回更新后的权重和偏置项。部分结果如下所示:



为了进行多次训练迭代,我们将反复执行上述过程。用一个简单 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 查询,对于有 100k 个实例的数据集,也很难执行超过 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。

仅通过十个迭代,我们的准确率就可达 93%(测试集上也差不多)。



如果我们把迭代次数加到 100 次,准确率高达 99%。


优化


下面是对本项目的总结。我们由此获得了哪些启发?如你所见,资源瓶颈决定了数据集的大小以及迭代执行的次数。除了祈求谷歌开放资源上限,我们还有如下优化手段来解决这个问题。


  • 创建中间表和多个 SQL 语句有助于增加迭代数。例如,前 10 次迭代的结果可以存储在一个中间表中。同一查询语句在执行下 10 次迭代时可以基于这个中间表。如此,我们就执行了 20 个迭代。这个方法可以反复使用,以应对更大的查询迭代。

  • 相比于在每一步增加外查询,我们应该尽可能的使用函数的嵌套。例如,在一个子查询中,我们可以同时计算 scores 和 probs,而不应使用 2 层嵌套查询。

  • 在上例中,所有的中间项都被保留直到最后一个外查询执行。其中有些项如 correct_logprobs 可以早些删除(尽管 SQL 引擎可能会自动的执行这类优化)。

  • 多尝试应用用户自定义的函数。如果感兴趣,你可以看看这个 BigQuery 的用户自定义函数的服务模型的项目(但是,无法使用 SQL 或者 UDFs 进行训练)。


意义


现在,让我们来看看基于深度学习的分布式 SQL 引擎的深层含义。 BigQuery、Presto  这类 SQL 仓库引擎的一个局限性在于,查询操作是在 CPU 而不是 GPU 上执行的。研究 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


作者:Harisankar Haridas

机器之心编译

参与:陈韵竹、思源

文章版权归原作者所有,转载仅供学习使用,不用于任何商业用途,如有侵权请留言联系删除,感谢合作。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/303700.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html5 控制鼠标移动,HTML5 Canvas随鼠标移动的引力粒子群

JavaScript语言&#xff1a;JaveScriptBabelCoffeeScript确定use strict;// Initial Setupvar canvas document.querySelector(canvas);var c canvas.getContext(2d);canvas.width innerWidth;canvas.height innerHeight;// Variablesvar mouse {x: innerWidth / 2,y: inn…

茫茫内存,我该如何用 windbg 找到你 ?

一&#xff1a;背景 1. 讲故事前天wx上有个朋友丢给我一个dump&#xff0c;让我帮忙鉴定一下某些敏感信息在内存中是否也是加密的&#xff0c;现在数据安全很重要&#xff0c;不仅数据库中的信息要加密&#xff0c;灌到内存后数据同样也需密文存储&#xff0c;随用随解密&#…

初中数学知识点总结_初中数学知识点总结大全_经典版_

初中数学必考知识点总结一、基本知识㈠、数与代数A、数与式&#xff1a;1、有理数有理数&#xff1a;①整数→正整数/0/负整数②分数→正分数/负分数数轴&#xff1a;①画一条水平直线&#xff0c;在直线上取一点表示0(原点)&#xff0c;选取某一长度作为单位长度&#xff0c;规…

官方的正则表达式组件 RegularExpressions (4) : 表达式选项

TRegExOption (roNone, //无roIgnoreCase, //不区分大小写roMultiLine, //多行模式; 可使 ^ 和 $ 匹配每个行首或行尾roExplicitCapture, //只捕获指定了名称或编号的子表达式roCompiled, //预编译表达式; 这在反复使用更有效率roSing…

一篇文章,带你了解 “机器学习工程师” 必备技能图谱

5月8日&#xff0c;谷歌召开一年一度的Google I/O大会。在现场演示的演示中&#xff0c;Google Assistant表现得自然流畅&#xff0c;电话那头的理发店员工丝毫没有察觉到自己竟然是在和AI对话&#xff01;阿里的王坚博士曾在一次主题演讲里谈到&#xff1a;「不要担心 AI 毁灭…

俄语使用计算机怎么说,计算机俄语常用词汇

计算机俄语常用词汇аксессуары 附件микрофон 话筒наушники 耳机MIDI клавиатура MIDI键盘MP3 плеер MP3播放器очки 眼镜иктофон 录音机ержатель копий 拷贝存放夹Принтеры 打印机матричный…

一日一技:在Ocelot网关中实现IdentityServer4密码模式(password)

概述IdentityServer4 是为ASP.NET Core 2.系列量身打造的一款基于 OpenID Connect 和 OAuth 2.0 认证框架。将identityserver部署在你的应用中&#xff0c;具备如下的特点可以为你的应用&#xff08;如网站、本地应用、移动端、服务&#xff09;做集中式的登录逻辑和工作流控制…

uibot在子程序执行js失败_使用 Node.js 将珍藏的 bash 脚本封装成命令行工具

阐述如何将一个常用的 bash 脚本融入 npm 生态之中&#xff0c;此处以最近遇到的一个 CR 提交脚本为例。背景作为程序猿&#xff0c;大家或多或少地都用过 GitHub 上的 merge request 功能。当然&#xff0c;除了这类 Code Review 方式&#xff0c;不少公司都有自己的 Code Rev…

Insus Binary Utility

一个将数据流转换为binary(二进制)数据小工具&#xff0c;返回字符串。可以在三层架构中的二层程序处理数据流。 使用时需要引用名称空间using Insus.NET; 类别名称InsusBinaryUtility&#xff0c;需要实例化。 下载地址&#xff1a;http://download.cnblogs.com/insus/library…

圆周率里有每个人的银行卡密码和生日?混知乎的程序员果然都是神一般的存在...

有人好奇&#xff0c;既然圆周率是无限不循环小数&#xff0c;会不会包括这个世界上的任何信息&#xff0c;包含了这个世界&#xff1f;能否包含“任何信息”不好说&#xff0c;但一个冷知识是&#xff0c;圆周率里有每个人的银行卡密码&#xff01;不信&#xff1f;看看这位来…

如何提高Debug效率

大家好&#xff0c;我是Z哥。可以不夸张地说&#xff0c;程序员可能有一半的时间都在修bug。虽说&#xff0c;根据28原则大部分bug都可以在搜索引擎上搜到&#xff08;业务性bug除外&#xff09;&#xff0c;但是往往剩下的那20%bug会花费我们80%的时间。虽然解决这个问题最好的…

中断原理在计算机中的应用,最新 计算机原理与应用 复习3-判断题

最新 计算机原理与应用 复习题目1.INTR、INTA和NMI信号都是与中断有关的信号。( ) √2.8088在访问存储器时&#xff0c;必须用HOLD和HLDA两条信号线指示出总线是否可用。( )3.在8088时序中&#xff0c;在时钟周期T4状态期间&#xff0c;S2S1S0的任何变化指示一个总线周期的开始…

baidumap vue 判断范围_懂一点前端—Vue快速入门

01. 什么是 VueVue (读音 /vjuː/&#xff0c;类似于 view) 是一套用于构建用户界面的渐进式框架&#xff0c;是当下很火的一个 JavaScript MVVM 库&#xff0c;是以 数据驱动和组件化 的思想构建的。MVVM 模式简述下图不仅概括了 MVVM 模式 (Model-View-ViewModel)&#xff0c…

open*** 结合pam_mysql认证 failed to authenticate: Permission denied

open***的认证有很多方式&#xff0c;比如证书认证&#xff0c;用户名密码认证&#xff0c;而用户名密码认证也可用通过两种方式实现&#xff0c;一是使用pam_mysql实现open***利用mysql认证&#xff0c;二是使用radius实现open***利用mysql认证&#xff0c;网上这种配置都很多…

用文本挖掘剖析近5万首《全唐诗》,发现了这些有趣的秘密

楔子近些年来&#xff0c;弘扬中华传统文化的现象级综艺节目不断涌现&#xff0c;如《中国汉字听写大会》、《中国成语大会》、《中国谜语大会》、《中国诗词大会》等&#xff0c;其背后的社会成因&#xff0c;在于人们对中国文化中最精致文字的膜拜心理&#xff0c;虽然浸淫于…

如何在 C#9 中使用顶级程序 (top-level)

当我们用 C# 进行编码的时候&#xff0c;总需要写很多的模板代码&#xff0c;即使是最简单的 console 程序,想象一下&#xff0c;如果去测试一个 类库 或者 API 的功能&#xff0c;通常你会用 Console 程序去实现&#xff0c;在开始工作的时候会发现你受到了 C# 标准模板的限制…

跨部门不配合工作_跨部门协作,队友总是“甩锅”,这三个方法教你快速避坑!...

在日常的工作中&#xff0c;你觉得跨部门沟通的时间占用了多少时间&#xff1f;前阵子我经常会看到有人抱怨&#xff0c;说跨部门沟通的工作实在太难了&#xff0c;同事总是推脱扯皮&#xff0c;领导交代下来的事情&#xff0c;要么说不是自己的职责&#xff0c;要么说以前没做…

web页面在线编辑功能

首先在web.config文件中添加 <system.web> <httpHandlers> <add verb"GET" path"FtbWebResource.axd" type"FreeTextBoxControls.AssemblyResourceHandler,FreeTextBox"/> </httpHandlers></system.web> 注…

html哪个是最大标题,在下面的HTML中,哪个是最大的标题( )

参考答案如下下中单选(2分) 新民主主义革命的三大法宝是( )单选(2分) 关于计算机为什么基于二进制数来实现&#xff0c;标题下列说法不正确的是单选(3分) “某些人天生具有一些特质&#xff0c;下中这些特质会使他们成为伟大的领导者。”这是以下哪种理论的观点?标题单选(3分)…

C#通过工厂模式,我把一大堆if干掉了

概述之前做微信项目的时候&#xff0c;会涉及到一个回调&#xff0c;大家都知道回调事件是很多类型的&#xff0c;一般是根据不同的类型在进行不同的逻辑处理。这样就会延伸出一个问题&#xff0c;就是入口处会有一大堆if判断。这样本身是没什么问题的&#xff0c;只是看起来比…