对于目的为学习embedding的模型。比如向量化User,Query间的关系。一侧使用Query相关特征,一侧使用User相关特征,进行数层全连接后,得到两个N维向量。向量间做Cosine来权衡其相关性。
该模型可以对目标进行深度的向量化,让向量本身针对特征有更好的迁移性,泛化性(而不是直接根据id做embedding,多个id之间没有语义联系,无法迁移)。在实际应用中,离线进行训练,然后线上大多数User或者Query可以用id映射到向量,然后应用于线上的相关性预估等目标(取决于离线的目标),少数新出现的user,query可以通过特征在线预估出其目标,然后再预估。这样可以减少相当多的资源消耗(缩减特征服务IO资源,预估时CPU资源),因为向量可以通过多种方式进行快速的检索。并且线上预估量由于离线embedding出了向量可以大幅降低
对模型本质的思考:
该模型非常依赖于数据分布,从embedding层参数到最终生成的向量,最终能得到的值很大依赖于训练样本中值的分布,参数不同的初始化值也会带来很大的不同:由于该模型user特征与post特征没有直接的hidden layer进行全连接,最后的连接层(cosine)并没有参数连接,所以user特征与post特征的组合关系不是通过模型参数显式学得的。(注:这里的显式指直接学习参数描述特征权重。可以对比LR,比如我设计了一个user feature A * post feature B的交叉特征,那么这个特征对应的weight参数值其实就是这个特征对label的权重影响,而这个模型本身没有任何参数去描述这种“直接”关系,所有关系都隐含在了embeding输出向量空间中,不同user,post向量的相对位置。在显式的学习中,样本输入后,更新参数可以直接通过偏导数更新到某个特征的参数上,而对于这种隐式的学习,更新的实际上是整个向量的值)
显式地学习特征权重相对更加直接也更容易,而要通过embedding 的结果来隐式地表达这种关系就对数据以及模型参数等要求更加苛刻。这也是为什么doc2vec,word2vec一类的模型,不同的使用者拿到不同的语料,用不同的参数训练相同的模型得到的效果常常大相径庭
深入到数据分布对模型的影响,对数据拟合的最佳结果是,每一对正例中的样本(user,poset)的向量都很相近,而负例中的样本的向量最好近似于反向。假设样本中正例为:(u1,p1),(u2,p1),(u2,p2),样本中的负例若有(u1,p2)便会造成空间上cosine距离的矛盾。考虑从初始位置输入一些正样本后,该user向量会朝着样本对应的post向量的位置调整,梯度逐渐减小,趋于收敛,而输入一条矛盾的负样本后,loss和梯度非常大,更新后的向量位置可能会大幅的变动。PS:相似关系具有传递性,即:(a,b)《 Related && (b,c) 《 Related ====> (a,c) 《 Realated
对于显式地学习特征权重的模型,这样的问题不复存在,每条正负例都是独立的样本,有独立的特征组合,只要加入ID特征等,特征空间有足够高的维度,总能近似去逼近最佳使其的线性可分的参数(LR本质是线性模型,通过特征组合带来了非线性
实验的观测:
对label=1的样本设置query=titile等于保证了不会有传递性的矛盾样本出现,在同一正负样本分布的样本上能做到square loss=0.1x。而真实数据一直维持在0.6左右(learning_rate=3e-4,batch_size=800)。
在数据随机分布的情况下,理论minimal趋近于对<u>帖子或者用户</u>在训练集上CTR的对应loss最小值。(导致头部推荐效应)
硬解决的思路:
1、使用对outliers相对不那么敏感的损失函数,se梯度太大
2、normalize error
最终解决方案:
使用inner product + sigmoid 映射值域到0,1 cross entropy做loss
原理:
1.点积是cosine * |a| * |b|,实际上在单纯的cosine上引入了向量长度的变量,<u>相对于cosine是个更弱的constrains</u>(单纯的cosine等于normalize去掉了长度,仅从夹角表述关系,上述的传递矛盾关系不可避免。)从训练样本(只训练了2 pass)误差可以观察:(uid, click, tid, 点积,sigmoid,cosine,相对长度)
- 虽然逻辑上都是embedding到高维空间使其分离(相似的相近,不相似远离),但是其优化的结果差别很大:可以看到sigmoid输出对概率的预测已经比较准确了,但是其cosine值其实分布就很广了,正例可能也就0.1,如果embedding出这样的样本,在cosine + mse上仍会有很高的loss,但是sigmoid+cross entropy上loss很低,并不会对这样的样本做太多惩罚,<u>而且这样的样本也可以通过cosine对正负例做区分了</u>。
- 比如第二三行,虽然第二行在cosine(-0.38)上比第三行cosine(-0.42)误差大,但是第二行样本训练出来的向量相对长度更长,所以其sigmoid输出0.008,其loss反而更小。即:通过改变相对的向量的长度,也可以优化其loss,而非必须改变cosine的距离。
2.2.换个角度看,user向量内积post向量,再接sigmoid+cross entropy,如果把一边当作常量(实际计算偏导的时候,也是将一边作为常数)实际上就等价于LR。例如,假设user_vec为LR输入,post_vec为LR的weight,则这个模型近似等价于最小化loss = floss( gsigmoid( user_input * weight1T)),其中weight1 = post_input * weight2T ,DNN学习的参数就是weight2部分,不过实际是更复杂的变换。而cosine+mse可以得到近似的表达,但是cosine函数为x * weight/|x| * |weight|,所以最终表达形式为:loss + lambda * ( weight1 -1)2 , lambda为拉格朗日乘子,可以类比为附加了个系数非常大的正则化项,便很容易under fitting。
3.mse可能有梯度弥散问题(PS:尝试过将sigmoid+ce换成mse也能较好地拟合,可能只是收敛慢一点)
PS:由于由Cosine变化为Inner-Product,所以检索的时候需要用不同于普通Cosine-Distance的检索方式,详细见:Maximum Inner Product Search(MIPS)