1,线性回归(Linear Regression)
线性回归,即使用多维空间中的一条直线拟合样本数据,如果样本特征为:
𝑥=(𝑥1,𝑥2,…,𝑥𝑛)x=(x1,x2,…,xn)x = ({x_1},{x_2},…,{x_n})
模型假设函数如下:
𝑦̂ =ℎ(𝑤,𝑏)=𝑤𝑇𝑥+𝑏,𝑤=(𝑤1,𝑤2,…,𝑤𝑛)y^=h(w,b)=wTx+b,w=(w1,w2,…,wn)\hat y = h(w,b) = {w^T}x + b,w = ({w_1},{w_2},…,{w_n})
以均方误差为模型损失,模型输入样本为 (x(1),y(1)),(x(2),y(2)),…,(x(m),y(m)),损失函数如下:
1 | 𝑙(𝑤,𝑏)=∑𝑗=1𝑚(𝑦̂ (𝑗)−𝑦(𝑗))2=∑𝑗=1𝑚(∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖+𝑏−𝑦(𝑗))l(w,b)=∑j=1m(y^(j)−y(j))2=∑j=1m(∑i=1nwixi(j)+b−y(j))l(w,b) = \sum\limits*{j = 1}^m {{{({{\hat y}^{(j)}} - {y^{(j)}})}^2}} = \sum\limits*{j = 1}^m {(\sum\limits\_{i = 1}^n {{w_i}x_i^{(j)} + b} - {y^{(j)}})} |
2,逻辑回归(Logistic Regression)
线性回归用于预测标记为连续的,尤其是线性或准线性的样本数据,但是有时样本的标记为离散的,最典型的情况莫过于标记为 0 或者 1,此时的模型被称为分类模型。
为了扩展线性回归到分类任务,需要一个函数将 (-∞,+∞) 映射到 (0,1)(或者(-1,1) 等任意两个离散值),函数最好连续可导,并且在自变量趋向于 -∞ 时无限趋近于因变量下限,在自变量趋向于 +∞ 时无限趋近于因变量上限。符合条件的函数有不少,比如 tanh 函数,logistic 函数。
如果映射函数采用 logistic 函数,设模型假设函数为样本预测分类概率,模型假设函数为:
1 | 𝑃(𝑦=1)=ℎ(𝑤,𝑏)=11+𝑒−ℎ𝑙𝑖𝑛𝑒𝑎𝑟(𝑤,𝑏)=11+𝑒−(𝑤𝑇𝑥+𝑏)𝑃(𝑦=0)=1−𝑃(𝑦=1)P(y=1)=h(w,b)=11+e−hlinear(w,b)=11+e−(wTx+b)P(y=0)=1−P(y=1)\begin{array}{l} P(y = 1) = h(w,b) = \frac{1}{{1 + {e^{ - {h_{linear}}(w,b)}}}} = \frac{1}{{1 + {e^{ - ({w^T}x + b)}}}}\\ P(y = 0) = 1 - P(y = 1) \end{array} |
另外,可以从另一个角度考虑从回归模型扩展为分类模型,令:
1 | ln(𝑃(𝑦=1)𝑃(𝑦=0))=ln(ℎ(𝑤,𝑏)1−ℎ(𝑤,𝑏))=ℎ𝑙𝑖𝑛𝑒𝑎𝑟(𝑤,𝑏)=𝑤𝑇𝑥+𝑏ln(P(y=1)P(y=0))=ln(h(w,b)1−h(w,b))=hlinear(w,b)=wTx+b\ln (\frac{{P(y = 1)}}{{P(y = 0)}}) = \ln (\frac{{h(w,b)}}{{1 - h(w,b)}}) = {h\_{linear}}(w,b) = {w^T}x + b |
同样可以得到映射函数为 logistic 函数的假设函数:
1 | ℎ(𝑤,𝑏)=11+𝑒−(𝑤𝑇𝑥+𝑏)=11+𝑒−(∑𝑖=1𝑛𝑤𝑖𝑥𝑖+𝑏)h(w,b)=11+e−(wTx+b)=11+e−(∑i=1nwixi+b)h(w,b) = \frac{1}{{1 + {e^{ - ({w^T}x + b)}}}} = \frac{1}{{1 + {e^{ - (\sum\limits_{i = 1}^n {{w_i}{x_i}} + b)}}}} |
使用(负)对数似然函数作为损失函数:
1 | 𝑙(𝑤,𝑏)=−log(∏𝑗=1𝑚𝑃(𝑦(𝑗)=1)𝑦(𝑗)𝑃(𝑦(𝑗)=0)(1−𝑦(𝑗)))=−∑𝑗=1𝑚(𝑦(𝑗)log𝑃(𝑦(𝑗)=1)+(1−𝑦(𝑗))log𝑃(𝑦(𝑗)=0))=−∑𝑗=1𝑚(𝑦(𝑗)log(𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖))+(1−𝑦(𝑗))log(1−𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖)),𝑔(𝑧)=11+𝑒−𝑧l(w,b)=−log(∏j=1mP(y(j)=1)y(j)P(y(j)=0)(1−y(j)))=−∑j=1m(y(j)logP(y(j)=1)+(1−y(j))logP(y(j)=0))=−∑j=1m(y(j)log(g(b+∑i=1nwixi(j)))+(1−y(j))log(1−g(b+∑i=1nwixi(j))),g(z)=11+e−z\begin{array}{l} l(w,b) = - \log (\prod\limits*{j = 1}^m {P{{({y^{(j)}} = 1)}^{{y^{(j)}}}}P{{({y^{(j)}} = 0)}^{(1 - {y^{(j)}})}}} )\\ = - \sum\limits*{j = 1}^m {({y^{(j)}}\log P({y^{(j)}} = 1) + (1 - {y^{(j)}})\log P({y^{(j)}} = 0))} \\ = - \sum\limits*{j = 1}^m {({y^{(j)}}\log (g(b + \sum\limits*{i = 1}^n {{w_i}x_i^{(j)}} )) + (1 - {y^{(j)}})\log (1 - g(b + \sum\limits\_{i = 1}^n {{w_i}x_i^{(j)}} ))} ,g(z) = \frac{1}{{1 + {e^{ - z}}}} \end{array} |
很多场景下,样本包含一些离散的 label 特征,这些特征很难连续量化,最简单的处理方式就是 one-hot,即将离散的每个数映射到多维空间中的一维。离散特征包含多少个可能值,转换后的特征就包含多少维。这样处理后样本特征的维数会变得非常巨大,另外大部分维度的值都是 0。
一方面,逻辑回归核心是一个线性模型,因此计算规模随着样本特征数的增长而线性增长,相较其他机器学习模型来说计算量(随特征数增长)的增长率较小;另一方面,逻辑回归假设函数与损失函数中,各个特征对于结果是独立起作用的,因此在样本数足够的前提下,也不会受到大量值为 0 的特征的干扰。因此特别适合这类场景下的分类问题。
3,因式分解机(Factorization Machine)
逻辑回归最大的优势是简单,最大的劣势也是太简单,没有考虑特征之间的相互关系,需要手动对特征进行预处理。因此就有了包含特征两两交叉项的假设函数:
1 | ℎ(𝑤,𝑏)=𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥𝑖+∑𝑖=1𝑛∑𝑗=𝑖+1𝑛𝜔𝑖𝑗𝑥𝑖𝑥𝑗),𝑔(𝑧)=11+𝑒−𝑧h(w,b)=g(b+∑i=1nwixi+∑i=1n∑j=i+1nωijxixj),g(z)=11+e−zh(w,b) = g(b + \sum\limits*{i = 1}^n {{w_i}{x_i}} + \sum\limits*{i = 1}^n {\sum\limits*{j = i + 1}^n {{\omega *{ij}}{x_i}} {x_j}} ),g(z) = \frac{1}{{1 + {e^{ - z}}}} |
此时有一个严重的问题,由于特征维数有可能是非常巨大的,很有可能训练样本中有一些特征组合 xixj(xi、xj 都不为 0)是完全不存在的,这样 ωij 就无法得到训练。实际使用模型时,如果出现特征组合 xixj,模型就无法正常工作。
为了减小训练计算量,也为了规避上面说的这种情况,我们引入辅助矩阵 v,n 为特征总维数,k 为超参数
𝑣∈𝑅𝑛∗𝑘v∈Rn∗kv \in {R^{n*k}}
然后使用 vvT 代替参数矩阵 ω,可得
1 | 𝜔𝑖𝑗=∑𝑟=1𝑘𝑣𝑖𝑟𝑣𝑇𝑟𝑗=∑𝑟=1𝑘𝑣𝑖𝑟𝑣𝑗𝑟ωij=∑r=1kvirvrjT=∑r=1kvirvjr{\omega _{ij}} = \sum\limits_{r = 1}^k {{v_{ir}}v*{rj}^T} = \sum\limits*{r = 1}^k {{v_{ir}}{v\_{jr}}} |
可得假设函数线性部分最后一项可化简为:
1 | ∑𝑖=1𝑛∑𝑗=𝑖+1𝑛𝜔𝑖𝑗𝑥𝑖𝑥𝑗=∑𝑖=1𝑛∑𝑗=𝑖+1𝑛∑𝑟=1𝑘𝑣𝑖𝑟𝑣𝑗𝑟𝑥𝑖𝑥𝑗=∑𝑟=1𝑘(∑𝑖=1𝑛∑𝑗=𝑖+1𝑛𝑣𝑖𝑟𝑣𝑗𝑟𝑥𝑖𝑥𝑗)=12∑𝑟=1𝑘(∑𝑖=1𝑛∑𝑗=1𝑛𝑣𝑖𝑟𝑣𝑗𝑟𝑥𝑖𝑥𝑗−∑𝑖=1𝑛𝑣𝑖𝑟𝑣𝑖𝑟𝑥𝑖𝑥𝑖)=12∑𝑟=1𝑘(∑𝑖=1𝑛𝑣𝑖𝑟𝑥𝑖∑𝑗=1𝑛𝑣𝑗𝑟𝑥𝑗−∑𝑖=1𝑛𝑣𝑖𝑟𝑣𝑖𝑟𝑥𝑖𝑥𝑖)=12∑𝑟=1𝑘((∑𝑖=1𝑛𝑣𝑖𝑟𝑥𝑖)2−∑𝑖=1𝑛(𝑣𝑖𝑟𝑥𝑖)2)∑i=1n∑j=i+1nωijxixj=∑i=1n∑j=i+1n∑r=1kvirvjrxixj=∑r=1k(∑i=1n∑j=i+1nvirvjrxixj)=12∑r=1k(∑i=1n∑j=1nvirvjrxixj−∑i=1nvirvirxixi)=12∑r=1k(∑i=1nvirxi∑j=1nvjrxj−∑i=1nvirvirxixi)=12∑r=1k((∑i=1nvirxi)2−∑i=1n(virxi)2)\begin{array}{l} \sum\limits*{i = 1}^n {\sum\limits*{j = i + 1}^n {{\omega _{ij}}{x*i}} {x_j}} \\ = \sum\limits*{i = 1}^n {\sum\limits*{j = i + 1}^n {\sum\limits*{r = 1}^k {{v_{ir}}{v*{jr}}{x_i}{x_j}} } } \\ = \sum\limits*{r = 1}^k {(\sum\limits*{i = 1}^n {\sum\limits*{j = i + 1}^n {{v_{ir}}{v*{jr}}{x_i}{x_j})} } } \\ = \frac{1}{2}\sum\limits*{r = 1}^k {(\sum\limits*{i = 1}^n {\sum\limits*{j = 1}^n {{v_{ir}}{v*{jr}}{x_i}{x_j}} } - \sum\limits*{i = 1}^n {{v_{ir}}{v*{ir}}{x_i}{x_i}} )} \\ = \frac{1}{2}\sum\limits*{r = 1}^k {(\sum\limits*{i = 1}^n {{v*{ir}}{x*i}} \sum\limits*{j = 1}^n {{v_{jr}}{x*j}} - \sum\limits*{i = 1}^n {{v_{ir}}{v*{ir}}{x_i}{x_i}} )} \\ = \frac{1}{2}\sum\limits*{r = 1}^k {({{(\sum\limits_{i = 1}^n {{v_{ir}}{x*i}} )}^2} - \sum\limits*{i = 1}^n {{{({v_{ir}}{x_i})}^2}} )} \end{array} |
同逻辑回归一样,令模型假设函数为样本预测分类概率,还是使用(负)对数似然函数为损失函数:
1 | 𝑙(𝑏,𝑤,𝑣)=−log(∏𝑗=1𝑚𝑃(𝑦(𝑗)=1)𝑦(𝑗)𝑃(𝑦(𝑗)=0)(1−𝑦(𝑗)))=−∑𝑗=1𝑚(𝑦(𝑗)log(ℎ(𝑤,𝑏)|𝑥=𝑥(𝑗))+(1−𝑦(𝑗))log(1−ℎ(𝑤,𝑏)|𝑥=𝑥(𝑗)))ℎ(𝑤,𝑏)|𝑥=𝑥(𝑗)=𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖+12∑𝑟=1𝑘((∑𝑖=1𝑛𝑣𝑖𝑟𝑥(𝑗)𝑖)2−∑𝑖=1𝑛(𝑣𝑖𝑟𝑥(𝑗)𝑖)2))𝑔(𝑧)=11+𝑒−𝑧l(b,w,v)=−log(∏j=1mP(y(j)=1)y(j)P(y(j)=0)(1−y(j)))=−∑j=1m(y(j)log(h(w,b)|x=x(j))+(1−y(j))log(1−h(w,b)|x=x(j)))h(w,b)|x=x(j)=g(b+∑i=1nwixi(j)+12∑r=1k((∑i=1nvirxi(j))2−∑i=1n(virxi(j))2))g(z)=11+e−z\begin{array}{l} l(b,w,v) = - \log (\prod\limits*{j = 1}^m {P{{({y^{(j)}} = 1)}^{{y^{(j)}}}}P{{({y^{(j)}} = 0)}^{(1 - {y^{(j)}})}}} ) = - \sum\limits*{j = 1}^m {({y^{(j)}}\log (h(w,b){|_{x = {x^{(j)}}}}) + (1 - {y^{(j)}})\log (1 - h(w,b){|_{x = {x^{(j)}}}}))} \\ h(w,b){|_{x = {x^{(j)}}}} = g(b + \sum\limits_{i = 1}^n {{w_i}x_i^{(j)}} + \frac{1}{2}\sum\limits*{r = 1}^k {({{(\sum\limits*{i = 1}^n {{v_{ir}}x*i^{(j)}} )}^2} - \sum\limits*{i = 1}^n {{{({v_{ir}}x_i^{(j)})}^2}} )} )\\ g(z) = \frac{1}{{1 + {e^{ - z}}}} \end{array} |
4,关于 FM 假设函数的讨论
仔细看 FM 假设函数线性部分最后一项的化简过程,会发现一个问题,这个 “化简” 过程仅仅是化简了式子的形式,其实并没有减少计算量,反倒是增加了计算量,如果假设函数不是:
1 | ℎ(𝑤,𝑏)=𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥𝑖+∑𝑖=1𝑛∑𝑗=𝑖+1𝑛𝜔𝑖𝑗𝑥𝑖𝑥𝑗)h(w,b)=g(b+∑i=1nwixi+∑i=1n∑j=i+1nωijxixj)h(w,b) = g(b + \sum\limits*{i = 1}^n {{w_i}{x_i}} + \sum\limits*{i = 1}^n {\sum\limits*{j = i + 1}^n {{\omega *{ij}}{x_i}} {x_j}} ) |
而是:
1 | ℎ(𝑤,𝑏)=𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥𝑖+∑𝑖=1𝑛∑𝑗=1𝑛𝜔𝑖𝑗𝑥𝑖𝑥𝑗)h(w,b)=g(b+∑i=1nwixi+∑i=1n∑j=1nωijxixj)h(w,b) = g(b + \sum\limits*{i = 1}^n {{w_i}{x_i}} + \sum\limits*{i = 1}^n {\sum\limits*{j = 1}^n {{\omega *{ij}}{x_i}} {x_j}} ) |
表面上看假设函数增加了若干项, 但经过化简后,可得线性部分最后一项为:
1 | ∑𝑖=1𝑛∑𝑗=1𝑛𝜔𝑖𝑗𝑥𝑖𝑥𝑗=∑𝑖=1𝑛∑𝑗=𝑖+1𝑛∑𝑟=1𝑘𝑣𝑖𝑟𝑣𝑗𝑟𝑥𝑖𝑥𝑗=∑𝑟=1𝑘((∑𝑖=1𝑛𝑣𝑖𝑟𝑥𝑖)2)∑i=1n∑j=1nωijxixj=∑i=1n∑j=i+1n∑r=1kvirvjrxixj=∑r=1k((∑i=1nvirxi)2)\sum\limits*{i = 1}^n {\sum\limits*{j = 1}^n {{\omega _{ij}}{x*i}} {x_j}} = \sum\limits*{i = 1}^n {\sum\limits*{j = i + 1}^n {\sum\limits*{r = 1}^k {{v_{ir}}{v*{jr}}{x_i}{x_j}} } } = \sum\limits*{r = 1}^k {({{(\sum\limits_{i = 1}^n {{v_{ir}}{x_i}} )}^2})} |
反倒是更简单了,为什么不用后面式子计算,而要用更复杂的前者呢?
这是由于 ω 的每一项并不是独立的,我们使用了 vvT 代替了 ω
1 | 𝜔𝑖𝑗=∑𝑟=1𝑘𝑣𝑖𝑟𝑣𝑇𝑟𝑗=∑𝑟=1𝑘𝑣𝑖𝑟𝑣𝑗𝑟ωij=∑r=1kvirvrjT=∑r=1kvirvjr{\omega _{ij}} = \sum\limits_{r = 1}^k {{v_{ir}}v*{rj}^T} = \sum\limits*{r = 1}^k {{v_{ir}}{v\_{jr}}} |
即可认为 vi=(vi1,vi2,…,vik) 代表了特征 i 与其他特征交叉关系的隐藏特征
如果不去掉交叉特征中的 xixi 项,则等同于 vi 不仅隐含了交叉关系,还隐含了 xi 的二次项,不符合我们的初衷
5,TensorFlow 概述
TensorFlow 即 tensor+flow,tensor 指数据,以任意维的类型化数组为具体形式,flow 则指数据的流向
描述 flow 有三个层次的概念:
- operation:是最基本的单元,每个 operation 获得 0 个或多个 tensor,产生 0 个或多个 tensor。operation 可能为获取一个常量 tf.constant(),获取一个变量 tf.get_variable() 或者进行一个操作,比如加法 tf.add()、乘法 tf.multiply()。
- graph:定义了数据流,由 tf.Graph() 初始化(一般情况下省略)。graph 由节点和节点间的连线构成,graph 中的节点即 operation。graph 是对数据流向组成的网络结构的定义,本身并不进行任何计算。
- session:定义了 graph 运行的环境,由 tf.Session() 初始化。session 中可以运行 graph 中一个或多个 operation,可以在运行前通过 feed 给 operation 输入数据,也可以通过返回值获取计算结果。
概念上讲,图中所有节点间的连线都是 tensor,但总需要有一些数据作为数据来源,描述这些数据来源的组件有以下几种类型:
- 常量:用 tf.constant() 定义,即运行过程中不会改变的数据,一般用来保存一些固有参数。常量可以被认为是一个 operation,输入为空,输出为常数(数组)。
- 变量:理论上广义的变量包含所有除常量以外的所有 tensor,这里的变量专指 tf.Variable()。值得注意的是,在 TensorFlow 中,小写开头的才是 operation,大写开头的都是类,因此 tf.Variable() 不是一个 operation,而是包含了一系列 operation,如初始化、赋值的类成员。每个 graph 运行前必须初始化所有使用到的变量。
- 占位符:用 tf.placeholder() 定义,是一种特殊类型的 operation,不需要初始化,但需要在运行中通过 feed 接收数据。
6,LR 的 TensorFlow 实现
首先需要定义输入样本的特征维度和接收输入样本特征和样本标签的占位符。
特征维度可以使用普通 python 变量指定,此时等同于使用 tf.constant 创建了一个常量
1 | FEATURE_NUM = 8 |
不可以通过使用 placeholder 指定特征维度
1 | m = tf.placeholder(tf.int32, shape=None) |
虽然 placeholder 初始 shape 中不能包含其他 tensor,但可以根据输入样本的列数动态指定
1 | x = tf.placeholder(tf.float32, shape=[None, None]) #或者可以不指定维数,输入shape=None,此时可以输入任意维数,任意大小的数据 |
接下来要定义放置 LR 假设函数中 w 与 b 的变量,在定义参数 w 前,需要定义一个 w 的初始值。对于 LR 来说,参数初始值为随机数或者全零都没关系,因为 LR 的损失函数一定是凸函数(求二阶导数可知),但一般情况下还是习惯采用随机数初始化。
1 | #常用的随机函数有random_uniform、random_normal和truncated_normal |
为了保持习惯用法,将一维向量 y 扩展为二维列向量,TensorFlow 中扩维可以使用 tf.expand_dims() 或者 tf.reshape():
1 | y_expand = tf.expand_dims(y, axis=1) #方法1,axis表示在原若干个维度的间隔中第几个位置插入新维度 |
可知:
𝑥∈𝑅𝑚∗𝑛,𝑦∈𝑅𝑚∗1,𝑤∈𝑅𝑛∗1,𝑏∈𝑅0x∈Rm∗n,y∈Rm∗1,w∈Rn∗1,b∈R0x \in {R^{mn}},y \in {R^{m1}},w \in {R^{n*1}},b \in {R^0}
回头看 LR 损失函数的定义
1 | 𝑙(𝑤,𝑏)=−∑𝑗=1𝑚(𝑦(𝑗)log(𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖))+(1−𝑦(𝑗))log(1−𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖)),𝑔(𝑧)=11+𝑒−𝑧l(w,b)=−∑j=1m(y(j)log(g(b+∑i=1nwixi(j)))+(1−y(j))log(1−g(b+∑i=1nwixi(j))),g(z)=11+e−zl(w,b) = - \sum\limits*{j = 1}^m {({y^{(j)}}\log (g(b + \sum\limits*{i = 1}^n {{w_i}x_i^{(j)}} )) + (1 - {y^{(j)}})\log (1 - g(b + \sum\limits\_{i = 1}^n {{w_i}x_i^{(j)}} ))} ,g(z) = \frac{1}{{1 + {e^{ - z}}}} |
在 TensorFlow 中,“+” 等同于 tf.add(),“-” 等同于 tf.subtract(),“*” 等同于 tf.multiply(),“/” 等同于 tf.div(),所有这些操作是 “逐项操作”:
- 如果两个操作数都为标量,则结果为标量
- 如果一个操作数为标量,另一个操作数为向量,则标量会分别与向量每一个元素逐个操作,得到结果
- 如果两个操作数为相同维度,每个维度大小相同的向量,则结果为向量每两个对应位置上的元素的操作得到的结果
- 如果两个操作数维度不相同,如果向量维度数为 a、b,并且 a>b,则 b 的各维度大小需要与 a 的高维度相对应,低维向量在高维向量的低维展开,操作得到结果
- 除上面几种情况,调用不合法
因此,在逻辑回归的损失函数中,我们无需关心 “b+”、“1-” 等操作,这些标量自然会展开到正确的维度。同样,tf.log()、tf.sigmoid()函数也会对输入向量每一个元素逐个操作,不会改变向量的维度和对应值的位置。
对于式子中的求和 Σ,在 TensorFlow 中有两种处理方式:
- 一种是矩阵乘法 tf.matmul(),如矩阵 a、b、c 关系为 c=tf.matmul(a,b),则可得:
1
2 > 𝑐𝑖𝑗=∑𝑘𝑎𝑖𝑘𝑏𝑘𝑗cij=∑kaikbkj{c*{ij}} = \sum\limits_k {{a*{ik}}{b\_{kj}}}
>
>
- 另一种是矩阵压缩函数 tf.reduce_sum()(求和)、tf.reduce_mean()(求平均)等函数。这些函数除了第一个参数为要操作的矩阵外,还有几个很有用的参数:axis 是要压缩的维度。对于一个二维矩阵来说,压缩维度为 0 意味着压缩行,生成列向量,压缩维度为 1 意味着压缩列,生成行向量,axis=None 意味着压缩成标量;keepdim 指压缩后是否保持原有维度,如果 keepdims=True,操作矩阵被压缩的维度长度会保持,但长度一定是 1
在损失函数中,先看最外层求和项(以 j 求和),可以看做 y 与 log 函数的结果的逐项相乘求和,因此 log 函数的输出向量形状必须与 y 或者 y 的转置一致。而 log 函数的输出向量形状取决于内部求和项(以 i 求和)。可以通过求和式得到合适的运算形式为 x*w,也可以通过形状来推理合适的运算形式,即:
1 | 𝑡𝑓.𝑚𝑎𝑡𝑚𝑢𝑙(𝑥,𝑤)∈𝑅𝑚∗1tf.matmul(x,w)∈Rm∗1tf.matmul(x,w) \in {R^{m\*1}} |
然后经过 log 函数得到的结果与 y 逐项相乘,再求压缩平均(此处之所以不是求和而是求平均,是为了避免样本数量对损失函数结果产生影响),可以得到假设函数、(负)似然函数、损失函数的运算式:
1 | y_float = tf.to_float(y_expand) |
还可以使用 TensorFlow 的损失函数计算 loss
TensorFlow 的损失函数主要有以下几个(下述所有公式假设样本数只有一个,多个的话需要取平均):
- log_loss:即交叉熵计算,设损失函数 l,假设函数 h,样本标签 y,二分类损失公式如下:
𝑙=−𝑦∗log(ℎ)−(1−𝑦)log(1−ℎ)l=−y∗log(h)−(1−y)log(1−h)l = - y*\log (h) - (1 - y)\log (1 - h)
多分类损失公式如下,注意可以有多个分类(分类数为 c)为正确分类:
𝑙=−∑𝑖𝑐(𝑦𝑖log(ℎ𝑖)−(1−𝑦𝑖)log(1−ℎ𝑖))l=−∑ic(yilog(hi)−(1−yi)log(1−hi))l = - \sum\limits_i^c {({y_i}\log ({h_i}) - (1 - {y_i})\log (1 - {h_i}))}
注意不是下式,这里跟一些其他库有区别:
1
2 > 𝑙=−∑𝑖𝑐𝑦𝑖log(ℎ𝑖)l=−∑icyilog(hi)l = - \sum\limits_i^c {{y_i}\log ({h_i})}
>
>
- sigmoid_cross_entropy:sigmoid + 交叉熵,与 log_loss 的差别在于先对输入预测值求 sigmoid 函数,然后再计算交叉熵。sigmoid 函数如下:
1
2 > 𝑔(𝑧)=11+𝑒−𝑧g(z)=11+e−zg(z) = \frac{1}{{1 + {e^{ - z}}}}
>
>
- softmax_cross_entropy:softmax + 交叉熵,设分类总数为 c,softmax 函数如下:
1
2 > 𝑔(𝑧𝑖)=𝑒𝑧𝑖∑𝑗𝑐𝑒𝑧𝑗g(zi)=ezi∑jcezjg({z_i}) = \frac{{{e^{{z_i}}}}}{{\sum\limits_j^c {{e^{{z_j}}}} }}
>
>
sparse_softmax_cross_entropy:计算方式与 softmax_cross_entropy,但输入样本标签为整数标签,而不是 one-hot 形式,这是唯一一个输入两个参数各维度大小不同的损失函数
mean_squared_error:均方误差损失函数,损失公式如下:
𝑙=(𝑦−ℎ)2l=(y−h)2l = {(y - h)^2}
- huber_loss:由于均方误差对误差比较大的点相当敏感,为了控制异常点对模型训练的影响,huber loss 对误差比较小的样本点使用二次项计算损失,对误差比较大的样本点使用线性方程计算损失,
1
2 > 𝑙={12(𝑦−ℎ)2,|𝑦−ℎ|<=𝑑12𝑑2+𝑑(|𝑦−ℎ|−𝑑),|𝑦−ℎ|>𝑑l={12(y−h)2,|y−h|<=d12d2+d(|y−h|−d),|y−h|>dl = \left\{ \begin{array}{l} \frac{1}{2}{(y - h)^2},|y - h| < = d\\ \frac{1}{2}{d^2} + d(|y - h| - d),|y - h| > d \end{array} \right.
>
>
- hinge_loss:hinge loss 不太在意预测的有多准确,而主要比较预测正确类别与错误类别的间隔,设损失函数 l,假设函数 h,样本标签 y,二分类损失公式如下:
1
2 > 𝑙={max(0,1−ℎ),𝑦=1max(0,1+ℎ),𝑦=0l={max(0,1−h),y=1max(0,1+h),y=0l = \left\{ \begin{array}{l} \max (0,1 - h),y = 1\\ \max (0,1 + h),y = 0 \end{array} \right.
>
>
设正确分类为 i,分类总数为 c,多分类损失公式如下:
1
2 > 𝑙=∑𝑗≠𝑖𝑐max(0,1+ℎ𝑗−ℎ𝑖)l=∑j≠icmax(0,1+hj−hi)l = \sum\limits\_{j \ne i}^c {\max (0,1 + {h_j} - {h_i})}
>
1 | 在此处,使用 tf.losses.log_loss() 或者 tf.losses.sigmoid_cross_entropy() 可以得到与上面运算式完全相同的结果,即: |
hypothesis = tf.sigmoid(tf.matmul(x, weight) + bais)
loss = tf.losses.log_loss(y_expand, hypothesis)
1 |
|
hypothesis = tf.matmul(x, weight) + bais
loss = tf.losses.sigmoid_cross_entropy(y_expand, hypothesis)
1 |
|
LEARNING_RATE = 0.02
optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)
training_op = optimizer.minimize(loss)
1 |
|
THRESHOLD = 0.5
predictions = tf.sign(hypothesis-THRESHOLD) #符号函数,判断向量对应位置的符号,输出对应位置为-1、0 或 1
labels = tf.sign(y_float-THRESHOLD)
corrections = tf.equal(predictions, labels) #比较向量对应位置的两个值,相等则输出对应位置为 True
accuracy = tf.reduce_mean(tf.cast(corrections, tf.float32))
1 |
|
THRESHOLD = 0.5
predictions = tf.to_int32(hypothesis-THRESHOLD)
corrections = tf.equal(predictions, y_expand)
accuracy = tf.reduce_mean(tf.cast(corrections, tf.float32))
1 |
|
tf.reset_default_graph() #清空 Graph
FEATURE_NUM = 8 #特征数量
with tf.name_scope(“input”):
x = tf.placeholder(tf.float32, shape=[None, FEATURE_NUM])
y = tf.placeholder(tf.int32, shape=[None])
with tf.name_scope(“lr”):
weight_init = tf.truncated_normal(shape=[FEATURE_NUM, 1],mean=0.0,stddev=1.0)
weight = tf.Variable(weight_init)
bais = tf.Variable([0.0])
y_expand = tf.expand_dims(y, axis=1)
hypothesis = tf.sigmoid(tf.matmul(x, weight) + bais)
with tf.name_scope(“loss”):
y_float = tf.to_float(y_expand)
likelyhood = -(y_floattf.log(hypothesis) + (1.0-y_float)(tf.log(1.0-hypothesis)))
loss = tf.reduce_mean(likelyhood, axis=0)
LEARNING_RATE = 0.02 #学习速率
with tf.name_scope(“train”):
optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)
training_op = optimizer.minimize(loss)
THRESHOLD = 0.5 #判断门限
with tf.name_scope(“eval”):
predictions = tf.sign(hypothesis-THRESHOLD)
labels = tf.sign(y_float-THRESHOLD)
corrections = tf.equal(predictions, labels)
accuracy = tf.reduce_mean(tf.cast(corrections, tf.float32))
init = tf.global_variables_initializer() #初始化所有变量
EPOCH = 10 #迭代次数
with tf.Session() as sess:
sess.run(init)
for i in range(EPOCH):
_training_op, _loss = sess.run([training_op, loss], feed_dict={x: np.random.rand(10,8), y: np.random.randint(2,size=10)})
_accuracy = sess.run([accuracy], feed_dict={x: np.random.rand(5,8), y: np.random.randint(2,size=5)})
print “epoch:”, i, _loss, _accuracy
1 |
|
FEATURE_NUM = 8 #特征数量
x = tf.placeholder(tf.float32, shape=[None, FEATURE_NUM])
y = tf.placeholder(tf.int32, shape=[None])
y_exband = tf.expand_dims(y, axis=1)
1 |
|
𝑙(𝑏,𝑤,𝑣)=−∑𝑗=1𝑚(𝑦(𝑗)log(ℎ(𝑤,𝑏)|𝑥=𝑥(𝑗))+(1−𝑦(𝑗))log(1−ℎ(𝑤,𝑏)|𝑥=𝑥(𝑗)))ℎ(𝑤,𝑏)|𝑥=𝑥(𝑗)=𝑔(𝑏+∑𝑖=1𝑛𝑤𝑖𝑥(𝑗)𝑖+12∑𝑟=1𝑘((∑𝑖=1𝑛𝑣𝑖𝑟𝑥(𝑗)𝑖)2−∑𝑖=1𝑛(𝑣𝑖𝑟𝑥(𝑗)𝑖)2))l(b,w,v)=−∑j=1m(y(j)log(h(w,b)|x=x(j))+(1−y(j))log(1−h(w,b)|x=x(j)))h(w,b)|x=x(j)=g(b+∑i=1nwixi(j)+12∑r=1k((∑i=1nvirxi(j))2−∑i=1n(virxi(j))2))\begin{array}{l} l(b,w,v) = - \sum\limits*{j = 1}^m {({y^{(j)}}\log (h(w,b){|*{x = {x^{(j)}}}}) + (1 - {y^{(j)}})\log (1 - h(w,b){|_{x = {x^{(j)}}}}))} \\ h(w,b){|_{x = {x^{(j)}}}} = g(b + \sum\limits*{i = 1}^n {{w_i}x_i^{(j)}} + \frac{1}{2}\sum\limits*{r = 1}^k {({{(\sum\limits_{i = 1}^n {{v_{ir}}x*i^{(j)}} )}^2} - \sum\limits*{i = 1}^n {{{({v_{ir}}x_i^{(j)})}^2}} )} ) \end{array}
1 |
|
HIDDEN_N = 5
bais = tf.Variable([0.0])
weight = tf.Variable(tf.random_normal([FEATURE_N, 1], 0.0, 1.0))
weight_mix = tf.Variable(tf.random_normal([FEATURE_N, HIDDEN_N], 0.0, 1.0))
1 |
|
𝑥∈𝑅𝑚∗𝑛,𝑣∈𝑅𝑛∗𝑘,𝑦∈𝑅𝑚∗1x∈Rm∗n,v∈Rn∗k,y∈Rm∗1x \in {R^{mn}},v \in {R^{nk}},y \in {R^{m*1}}
1 | 先尝试矩阵乘法降维,可得: |
𝑥𝑣∈𝑅𝑚∗𝑘,(𝑥𝑣)𝑎𝑏=∑𝑖=1𝑛𝑥(𝑎)𝑖𝑣𝑖𝑏xv∈Rm∗k,(xv)ab=∑i=1nxi(a)vibxv \in {R^{m*k}},{(xv){ab}} = \sum\limits{i = 1}^n {xi^{(a)}{v{ib}}}
1 | 可见正好与二次项的第一项形式一致,可以通过以下方式计算第一项,一个 m\*k 的矩阵,得到结果后可以通过先压缩第 1 维再压缩第 0 维,得到损失函数结果(0 维标量) |
x_weight_mix = tf.matmul(x, weight_mix)
x_weight_mix_square = tf.square(x_weight_mix)
1 |
|
∑𝑗=1𝑚∑𝑟=1𝑘∑𝑖=1𝑛(𝑥(𝑗)𝑖𝑣𝑖𝑟)2∑j=1m∑r=1k∑i=1n(xi(j)vir)2\sum\limits*{j = 1}^m {\sum\limits*{r = 1}^k {\sum\limits*{i = 1}^n {{{(x_i^{(j)}{v*{ir}})}^2}} } }
1 | 即需要得到每个 j、r、i 的组合,再平方,再依次求和,可知这必须是一个三维矩阵才能完成运算。因此,如果能设计一个三维矩阵 d 满足如下就条件,就好计算了: |
𝑑𝑗𝑟𝑖=𝑥(𝑗)𝑖𝑣𝑖𝑟,𝑑∈𝑅𝑚∗𝑘∗𝑛djri=xi(j)vir,d∈Rm∗k∗n{d{jri}} = x_i^{(j)}{v{ir}},d \in {R^{mkn}}
1 | 由于三维矩阵 d 的每项不涉及求和,只有两个数相乘,因此最直接的方式是扩展 x、v 的维度与 d 相同,然后直接逐项相乘,为方便计算,调整三维矩阵 d 的定义: |
𝑑𝑗𝑖𝑟=𝑥(𝑗)𝑖𝑣𝑖𝑟,𝑑∈𝑅𝑚∗𝑛∗𝑘djir=xi(j)vir,d∈Rm∗n∗k{d{jir}} = x_i^{(j)}{v{ir}},d \in {R^{mnk}}
1 |
|
𝑑𝑗𝑖𝑟=𝑥(𝑛𝑒𝑤),(𝑗)𝑖𝑟𝑣(𝑛𝑒𝑤),𝑗𝑖𝑟,𝑑∈𝑅𝑚∗𝑛∗𝑘𝑥(𝑛𝑒𝑤),(𝑗)𝑖𝑟=𝑥(𝑗)𝑖,𝑥(𝑛𝑒𝑤)∈𝑅𝑚∗𝑛∗𝑘𝑣(𝑛𝑒𝑤),𝑗𝑖𝑟=𝑣𝑖𝑟,𝑣(𝑛𝑒𝑤)∈𝑅𝑚∗𝑛∗𝑘djir=x(new),ir(j)v(new),jir,d∈Rm∗n∗kx(new),ir(j)=xi(j),x(new)∈Rm∗n∗kv(new),jir=vir,v(new)∈Rm∗n∗k\begin{array}{l} {d{jir}} = {x{(new),}}{ir}^{(j)}{v{(new)}}{,jir},d \in {R^{mnk}}\ {x{(new),}}{ir}^{(j)} = x_i^{(j)},{x{(new)}} \in {R^{mnk}}\ {v{(new)}}{,jir} = {v{ir}},{v{(new)}} \in {R^{mnk}} \end{array}
1 |
|
SAMPLEN = tf.shape(x)[0]
x = tf.reshape(x, [SAMPLEN,FEATURE_N,1]) #SAMPLENFEATURE_N*1
x\_ = tf.tile(x, [1,1,HIDDEN_N]) #SAMPLENFEATURE_NHIDDENN
w = tf.reshape(weightmix, [1,FEATURE_N,HIDDEN_N]) #1FEATURE_NHIDDEN_N
w\_ = tf.tile(x, [SAMPLE_N,1,1]) #SAMPLE_NFEATURE_NHIDDEN_N
embeddings = tf.multiply(x, w) #SAMPLE_NFEATURE_NHIDDEN_N
embeddings_square = tf.square(embeddings) #SAMPLE_NFEATURE_NHIDDEN_N
embeddings_square_sum = tf.reduce_sum(embeddings_square, 1) #SAMPLE_N\HIDDEN_N
1 |
|
embeddings_sum = tf.reduce_sum(embeddings, 1) #SAMPLE_NHIDDENT_N
embeddings_sum_square = tf.square(embeddings_sum) #SAMPLE_NHIDDENT_N
1 |
|
z = bais + tf.matmul(X, weight) + 1.0/2.0*tf.reduce_sum(tf.subtract(embeddings_sum_square,embeddings_square_sum), 1, keepdims=True)
hypothesis = tf.sigmoid(z)
1 |
|
z = bais + tf.matmul(x, weight) + 1.0/2.0*tf.reducesum(tf.subtract(embeddings_sum_square,embeddings_square_sum), 1, keepdims=True)
z = tf.clipby_value(z,-4.0,4.0)
hypothesis = tf.sigmoid(z)
1 |
|
tf.reset_default_graph() #清空 Graph
FEATURE_NUM = 8 #特征数量
with tf.name_scope(“input”):
x = tf.placeholder(tf.float32, shape=[None, FEATURE_NUM])
y = tf.placeholder(tf.int32, shape=[None])
y_expand = tf.expand_dims(y, axis=1)
HIDDEN_NUM = 5 #隐藏特征维度
with tf.name_scope(“fm”):
bais = tf.Variable([0.0])
weight = tf.Variable(tf.random_normal([FEATURE_NUM, 1], 0.0, 1.0))
weight_mix = tf.Variable(tf.random_normal([FEATURE_NUM, HIDDEN_NUM], 0.0, 1.0))
SAMPLE_NUM = tf.shape(x)[0] #获取样本数
x_ = tf.reshape(x, [SAMPLE_NUM,FEATURE_NUM,1]) #SAMPLE_NUM*FEATURE_NUM*1
x__ = tf.tile(x_, [1,1,HIDDEN_NUM]) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
w_ = tf.reshape(weight_mix, [1,FEATURE_NUM,HIDDEN_NUM]) #1*FEATURE_NUM*HIDDEN_NUM
w__ = tf.tile(w_, [SAMPLE_NUM,1,1]) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
embeddings = tf.multiply(x__, w__) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
embeddings_sum = tf.reduce_sum(embeddings, 1) #SAMPLE_NUM*HIDDEN_NUM
embeddings_sum_square = tf.square(embeddings_sum) #SAMPLE_NUM*HIDDEN_NUM
embeddings_square = tf.square(embeddings) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
embeddings_square_sum = tf.reduce_sum(embeddings_square, 1) #SAMPLE_NUM*HIDDEN_NUM
z = bais + tf.matmul(x, weight) + 1.0/2.0*tf.reduce_sum(tf.subtract(embeddings_sum_square,embeddings_square_sum), 1, keepdims=True)
z_ = tf.clip_by_value(z,-4.0,4.0)
hypothesis = tf.sigmoid(z_)
with tf.name_scope(“loss”):
loss = tf.losses.log_loss(y_expand, hypothesis)
LEARNING_RATE = 0.02 #学习速率
with tf.name_scope(“train”):
optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)
training_op = optimizer.minimize(loss)
THRESHOLD = 0.5 #判断门限
with tf.name_scope(“eval”):
predictions = tf.to_int32(hypothesis-THRESHOLD)
corrections = tf.equal(predictions, y_expand)
accuracy = tf.reduce_mean(tf.cast(corrections, tf.float32))
init = tf.global_variables_initializer() #初始化所有变量
EPOCH = 10 #迭代次数
with tf.Session() as sess:
sess.run(init)
for i in range(EPOCH):
_training_op, _loss = sess.run([training_op, loss], feed_dict={x: np.random.rand(10,8), y: np.random.randint(2,size=10)})
_accuracy = sess.run([accuracy], feed_dict={x: np.random.rand(5,8), y: np.random.randint(2,size=5)})
print “epoch:”, i, _loss, _accuracy
1 |
|
FM 模型
class FmModel(object):
def init(self, x, y, feature_num, hidden_num):
self.x = x
self.y = y
self.feature_num = feature_num #获取特征数,这个值要建 Variable,所以不能动态获取
self.sample_num = tf.shape(x)[0] #获取样本数
self.hidden_num = hidden_num #获取隐藏特征维度
self.bais = tf.Variable([0.0])
self.weight = tf.Variable(tf.random_normal([self.feature_num, 1], 0.0, 1.0))
self.weight_mix = tf.Variable(tf.random_normal([self.feature_num, self.hidden_num], 0.0, 1.0))
x_ = tf.reshape(self.x, [self.sample_num,self.feature_num,1]) #SAMPLE_NUM*FEATURE_NUM*1
x__ = tf.tile(x_, [1,1,self.hidden_num]) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
w_ = tf.reshape(self.weight_mix, [1,self.feature_num,self.hidden_num]) #1*FEATURE_NUM*HIDDEN_NUM
w__ = tf.tile(w_, [self.sample_num,1,1]) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
embeddings = tf.multiply(x__, w__) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
embeddings_sum = tf.reduce_sum(embeddings, 1) #SAMPLE_NUM*HIDDEN_NUM
embeddings_sum_square = tf.square(embeddings_sum) #SAMPLE_NUM*HIDDEN_NUM
embeddings_square = tf.square(embeddings) #SAMPLE_NUM*FEATURE_NUM*HIDDEN_NUM
embeddings_square_sum = tf.reduce_sum(embeddings_square, 1) #SAMPLE_NUM*HIDDEN_NUM
z = self.bais + tf.matmul(self.x, self.weight) + 1.0/2.0*tf.reduce_sum(tf.subtract(embeddings_sum_square,embeddings_square_sum), 1, keepdims=True)
z_ = tf.clip_by_value(z,-4.0,4.0)
self.hypothesis = tf.sigmoid(z_)
y_expand = tf.expand_dims(self.y, axis=1)
self.loss = tf.losses.log_loss(y_expand, self.hypothesis)
1 |
|
FM 模型
class FmModel(object):
def init(self, x, y, feature_num, hidden_num):
…
embeddings = tf.multiply(x, w, ) #SAMPLE_NUMFEATURE_NUMHIDDEN_NUM
…
HIDDEN_NUM = 5 #隐藏特征维度
with tf.name_scope(“fm”):
fm = FmModel(x, y, FEATURE_NUM, HIDDEN_NUM)
embeddings = tf.get_default_graph().get_tensor_by_name(“fm/embeddings:0”) #注意 name_scope 与后面的冒号和序号
1 |
|
tf.reset_default_graph() #清空 Graph
FEATURE_NUM = 8 #特征数量
with tf.name_scope(“input”):
x = tf.placeholder(tf.float32, shape=[None, FEATURE_NUM])
y = tf.placeholder(tf.int32, shape=[None])
HIDDEN_NUM = 5 #隐藏特征维度
with tf.name_scope(“fm”):
fm = FmModel(x, y, FEATURE_NUM, HIDDEN_NUM)
LEARNING_RATE = 0.02 #学习速率
with tf.name_scope(“train”):
optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)
training_op = optimizer.minimize(fm.loss)
THRESHOLD = 0.5 #判断门限
with tf.name_scope(“eval”):
y_expand = tf.expand_dims(y, axis=1)
predictions = tf.to_int32(fm.hypothesis-THRESHOLD)
corrections = tf.equal(predictions, fm.y_expand)
accuracy = tf.reduce_mean(tf.cast(corrections, tf.float32))
init = tf.global_variables_initializer() #初始化所有变量
EPOCH = 10 #迭代次数
with tf.Session() as sess:
sess.run(init)
for i in range(EPOCH):
_training_op, _loss = sess.run([training_op, fm.loss], feed_dict={x: np.random.rand(10,8), y: np.random.randint(2,size=10)})
_accuracy = sess.run([accuracy], feed_dict={x: np.random.rand(5,8), y: np.random.randint(2,size=5)})
print “epoch:”, i, _loss, _accuracy
1 |
|
FM 模型
class FmModel(object):
def init(self, i, x, y, feature_num, valid_num, hidden_num):
self.i = i
self.x = x
self.y = y
self.feature_num = feature_num #获取特征数,这个值要建 Variable,所以不能动态获取
self.valid_num = valid_num #获取有效特征数,这个值要建 Variable,所以不能动态获取
self.sample_num = tf.shape(x)[0] #获取样本数
self.hidden_num = hidden_num #获取隐藏特征维度
self.bais = tf.Variable([0.0])
self.weight = tf.Variable(tf.random_normal([self.feature_num, 1], 0.0, 1.0))
self.weight_mix = tf.Variable(tf.random_normal([self.feature_num, self.hidden_num], 0.0, 1.0))
x_ = tf.reshape(self.x, [self.sample_num,self.valid_num,1]) #SAMPLE_NUM*VALID_NUM*1
w_ = tf.nn.embedding_lookup(self.weight, self.i) #SAMPLE_NUM*VALID_NUM*1
expressings = tf.multiply(x_, w_) #SAMPLE_NUM*VALID_NUM*1
expressings_reduce = tf.reshape(self.x, [self.sample_num,self.valid_num]) #SAMPLE_NUM*VALID_NUM
x__ = tf.tile(x_, [1,1,self.hidden_num]) #SAMPLE_NUM*VALID_NUM*HIDDEN_NUM
w__ = tf.nn.embedding_lookup(self.weight_mix, self.i) #SAMPLE_NUM*VALID_NUM*HIDDEN_NUM
embeddings = tf.multiply(x__, w__) #SAMPLE_NUM*VALID_NUM*HIDDEN_NUM
embeddings_sum = tf.reduce_sum(embeddings, 1) #SAMPLE_NUM*HIDDEN_NUM
embeddings_sum_square = tf.square(embeddings_sum) #SAMPLE_NUM*HIDDEN_NUM
embeddings_square = tf.square(embeddings) #SAMPLE_NUM*VALID_NUM*HIDDEN_NUM
embeddings_square_sum = tf.reduce_sum(embeddings_square, 1) #SAMPLE_NUM*HIDDEN_NUM
z = self.bais + \
tf.reduce_sum(expressings_reduce, 1, keepdims=True) + \
1.0/2.0*tf.reduce_sum(tf.subtract(embeddings_sum_square,embeddings_square_sum), 1, keepdims=True)
z_ = tf.clip_by_value(z,-4.0,4.0)
self.hypothesis = tf.sigmoid(z_)
self.y_expand = tf.expand_dims(self.y, axis=1)
self.loss = tf.losses.log_loss(self.y_expand, self.hypothesis)
tf.reset_default_graph() #清空 Graph
VALID_NUM = 8 #有效特征数量
with tf.name_scope(“input”):
i = tf.placeholder(tf.int32, shape=[None, VALID_NUM])
x = tf.placeholder(tf.float32, shape=[None, VALID_NUM])
y = tf.placeholder(tf.int32, shape=[None])
y_expand = tf.expand_dims(y, axis=1)
FEATURE_NUM = 20 #特征数量
HIDDEN_NUM = 5 #隐藏特征维度
with tf.name_scope(“fm”):
fm = FmModel(i, x, y, FEATURE_NUM, VALID_NUM, HIDDEN_NUM)
LEARNING_RATE = 0.02 #学习速率
with tf.name_scope(“train”):
optimizer = tf.train.GradientDescentOptimizer(LEARNING_RATE)
training_op = optimizer.minimize(fm.loss)
THRESHOLD = 0.5 #判断门限
with tf.name_scope(“eval”):
predictions = tf.to_int32(fm.hypothesis-THRESHOLD)
corrections = tf.equal(predictions, fm.y_expand)
accuracy = tf.reduce_mean(tf.cast(corrections, tf.float32))
init = tf.global_variables_initializer() #初始化所有变量
EPOCH = 10 #迭代次数
with tf.Session() as sess:
sess.run(init)
for epoch in range(EPOCH):
_training_op, _loss = sess.run([training_op, fm.loss],
feed_dict={i: np.array([np.random.choice(20, 8) for cnt in range(10)]), x: np.random.rand(10,8), y: np.random.randint(2,size=10)})
_accuracy = sess.run([accuracy],
feed_dict={i: np.array([np.random.choice(20, 8) for cnt in range(5)]), x: np.random.rand(5,8), y: np.random.randint(2,size=5)})
print “epoch:”, epoch, _loss, _accuracy
1 |
|