最近都面临一个问题是, 要用纯 sql 来实现所有的逻辑, 其实 union 呀, 嵌套, 子查询呀, 这些都还好, 但那带有逻辑判断的, 这就整不好整了, 就多分支的, 再分支这种... 也不知为啥, 一年前写 sql , 都没有超过 20行... 自从搞了一段时间 BI , 这尼玛, 现在随便写的几百行 sql... 逻辑其实不算复杂, 就是一层层嵌套, 子查询, 当做一个表取 as 别名.... 这尼玛, 越写越多... 总是感觉越发复杂和, 渐渐明白, ETL 的那些兄弟的意义就是, 帮忙做中间表, 做宽表, 做调度 .. 虽然这部分,我自己写程序也能做, 但现在是没有接入编程语言.. 要一通 sql 来做.. 实在有点难受... 看了一个兄弟, 一个逻辑, 2000多行的sql ...我在想这些人是中什么样的境界....
都停滞好几天自己的学习了, 困惑中, 就很难去集中精力搞自己的事情, 因此周末还要给续上一点的. 这篇主要学一波 张量的直观用.
标量
标量在 TF 中, 就是一个 维度为 0 , shape 为 [ ] 的数字. 我觉得这些概念, 回归数学概念来理解会更加地直观和自然. 标量典型应用是一些指标, 如误差值的测量表示, 如 准确度 (Accuracy) , 精度 (Precision) , 召回率 (Recall) 等.
# 随机模拟网络输出
out = tf.random.uniform([1, 10])
# 随机构造真实样本标签
y = tf.constant([1, 3, 2, 5, 5])
# one-hot 编码
y = tf.one_hot(y, depth=10)
print(y)
# 计算每个样本的MSE, 是个向量
loss = tf.keras.losses.mse(y, out)
print('loss:', loss)
# 均方误 MSE, 是个标量
loss = tf.reduce_mean(loss)
print("loss_mes:", loss)
tf.Tensor(
[[0. 1. 0. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 1. 0. 0. 0. 0. 0. 0.]
[0. 0. 1. 0. 0. 0. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]
[0. 0. 0. 0. 0. 1. 0. 0. 0. 0.]], shape=(5, 10), dtype=float32)
loss: tf.Tensor([0.14861506 0.17281498 0.18253241 0.07795892 0.07795892], shape=(5,), dtype=float32)
loss_mes: tf.Tensor(0.13197607, shape=(), dtype=float32)
向量
向量在 TF 中, 就一个, 维度为1, shape 为 [n, ] 的一维数组. 跟数学上的向量还是有点区别的. 数学中更加广泛, 矩阵, 包括张量, 从线性代数的角度看, 都是向量, 当然线性代数的核心研究点, 如线性空间, 线性变换 .. 这些的基础点都是向量. 矩阵也是向量, 当然,矩阵也可以看做一个函数, 这里不扯了, 这些在我前几年上大学就已经弄明白了, 已经毕业了哈...
在神经网络中, 向量是做为一个特别重要的数据载体, 因为网络的输入, 要求必须是一个 一维的向量, 然后进行 WX + b, 再激活函数, softmax ... 误差反向传播 等... 这些都很基础不谈了, 然后用代码来模拟一下, 全连接层 和 偏置 的小示范, 大家就明白了.
# z = wx 模拟激活函数的输入 z
z = tf.random.normal([4,2])
# 创建偏置向量
b = tf.ones([2])
# 累加上偏置向量
z + b
<tf.Tensor: id=45, shape=(4, 2), dtype=float32, numpy=
array([[2.5850847 , 1.0067246 ],
[2.1504483 , 1.7368696 ],
[2.1437292 , 0.74127305],
[0.37799203, 1.2619207 ]], dtype=float32)>
这段代码, 我搬运的时候, 也是看着很奇怪的. z 的 shap 是 (4, 2) , 而 b 的 shape 是 (2, ) 这两兄弟的 shape 都不一会竟然能相加, 诡异得很... 先记下这个问题, 后面查波资料再说吧..
在 TF 中, 通过高层接口类 Dense ( ) 来创建网络层, 张量 W 和 b 的内部, 由类自动创建并管理. 可以通过全连接层的 bias 成员变量(属性) 来查看偏置 b. 比如来创建一个, 节点数为 4, 输出节点为 3 的线性网络层, 即其 bias 的长度为 3嘛, 代码是这样玩的.
# 创建一层 Wx + b, 输出节点为 3
my_layer = tf.keras.layers.Dense(3)
# 通过 build 方法创建 W, b 张量, 输入节点为 4
my_layer.build(input_shape=(4, 2))
my_layer.bias
<tf.Variable 'bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>
可以看到, 类的偏置 bias 是长度为 3 的向量, 初始值全为 0. 同时, bias 的类型为 Variable, 因为 W, b 都是待优化参数嘛, 需要进行自动求导的哦.
矩阵
在神经网络中, 层与层之间的, 权值 W, 就是一个 权值矩阵嘛, 不多说了, 直接看段代码就明白了.
# 2行4列, 即表示 2个样本, 每个样本有 4 个特征
x = tf.random.normal([2, 4])
# 定义权值矩阵 W 矩阵乘法: mxn * nxp -> m x p
w = tf.ones([4, 3])
# 定义偏置向量 b, 长度为 3 即 2x4 * 4*3 -> 2*3
b = tf.zeros([3])
# @ 就点乘 dot 的作用, 为了简化, 这里没设置 激活函数哈
o = x @ w + b
o
<tf.Tensor: id=83, shape=(2, 3), dtype=float32, numpy=
array([[0.4589759, 0.4589759, 0.4589759],
[1.8880281, 1.8880281, 1.8880281]], dtype=float32)>
这就实现了一个线性变换的网络层, 当然这么没有激活函数, 一般自己写个就好啦, 非线性, 映射到 0-1 之间的函数, 随便弄. (sigma(X@W + b)) 也可以被称为全连接层. 在 TF 中通过 tf.keras.layers.Dense( ) 来实现. 像这里的激活函数 sigma 为空时, 该全连接层也称为 线性层.
最常用的激活函数 sigmoid: (sigma(x) = frac {1}{1+e^{-x}})
作用是把一个值, 映射到 (0-1) , 逻辑回归会用到, 用其来作为一个概率值来弄的, 反正也不难, 我都推导 好多次了.
{再来一把, 通过 Dense() 类来创建一个, 输入 4 个节点, 输出 3 个节点 的网络层.
# 输出节点数为 3
my_layer = tf.keras.layers.Dense(3)
# 全连接层输入节点为 4
my_layer.build(input_shape=(2,4))
# 查看权值矩阵的 W, 和 b
print('W:',my_layer.kernel)
print('b:', my_layer.bias)
W:
<tf.Variable 'kernel:0' shape=(4, 3) dtype=float32, numpy=
array([[-0.0026896 , 0.8679098 , -0.91239446],
[-0.38716274, -0.23643166, -0.20897073],
[-0.91767067, 0.7481936 , 0.45598388],
[-0.15754491, -0.04295635, 0.37723374]], dtype=float32)>
b:
<tf.Variable 'bias:0' shape=(3,) dtype=float32, numpy=array([0., 0., 0.], dtype=float32)>
还有 3维张量, 4维张量, 就先不讨论, 都是后面的重点关注对象嘛, NLP 呀, 图片这, 这些都是都是要用 张量 来表示的, 我自己觉得, 理解起来, 不是那么容易, 包括现在也更多停留在, 初步理解的层面上, 它不像对编程的数组, 列表, 字符串,这些有比较深刻的认识, 就不在这里瞎误导了.
初步了解了一波, TF 的 一些 "数据结构" , 就已经是我比较满意的了, 标量, 向量, 矩阵, 张量这些, 其实从编程和数学的视角来看待, 我是觉得非常自然的, 也是非常佩服我们人类的智慧, 能够让只能识别高低电压的 0, 1 计算机去尝试理解, 人类的自然语言, 大自然的画卷图案. 与我自己在学习中, 也非常好奇和向往. 虽然我每天工作都是 搬砖, 写脚本, 拼 sql , 背锅这些小事情, 但, 学无止境嘛, 这也是我受大学教育给的我最为重要的知识.