0%

使用GBDT算法构造特征

​ Facebook 2014年的文章介绍了通过GBDT解决LR的特征组合问题。[1]GBDT思想对于发现多种有区分性的特征和组合特征具有天然优势,可以用来构造新的组合特征。

​ 在这篇论文中提出可以使用GBDT各棵数输出节点的索引号来作为新的特征,对各个树渠道的索引号做one-hot编码,然后与原始的特征一起新的特征输入到模型中往往会起到不错的效果

实践情况

​ 1.本人使用这种方式在ctr预估中已经进行过实验,准确率提升2%

​ 2.美团在外卖预计送达时间预测中进行了实验,各个时段平均偏差减少了3%

(1) 超参数选择

a. 首先为了节点分裂时质量和随机性,分裂时所使用的最大特征数目为√n。
b. GBDT迭代次数(树的数量)。

  • 树的数量决定了后续构造特征的规模,与学习速率相互对应。通常学习速率设置较小,但如果过小,会导致迭代次数大幅增加,使得新构造的特征规模过大。
  • 通过GridSearch+CrossValidation可以寻找到最合适的迭代次数+学习速率的超参组合。
    c. GBDT树深度需要足够合理,通常在4~6较为合适。
  • 虽然增加树的数量和深度都可以增加新构造的特征规模。但树深度过大,会造成模型过拟合以及导致新构造特征过于稀疏。

(2)训练方案

​ 将训练数据随机抽样50%,一分为二。前50%用于训练GBDT模型,后50%的数据在通过GBDT输出样本在每棵树中输出的叶子节点索引位置,并记录存储,用于后续的新特征的构造和编码,以及后续模型的训练。如样本x通过GBDT输出后得到的形式如下:x → [25,20,22,….,30,28] ,列表中表示样本在GBDT每个树中输出的叶子节点索引位置。

​ 由于样本经过GBDT输出后得到的x → [25,20,22,….,30,28] 是一组新特征,但由于这组新特征是叶子节点的ID,其值不能直接表达任何信息,故不能直接用于ETA场景的预估。为了解决上述的问题,避免训练过程中无用信息对模型产生的负面影响,需要通过独热码(OneHotEncoder)的编码方式对新特征进行处理,将新特征转化为可用的0-1的特征。

​ 以图5中的第一棵树和第二棵树为例,第一棵树共有三个叶子节点,样本会在三个叶子节点的其中之一输出。所以样本在该棵树有会有可能输出三个不同分类的值,需要由3个bit值来表达样本在该树中输出的含义。图中样本在第一棵树的第一个叶子节点输出,独热码表示为{100};而第二棵树有四个叶子节点,且样本在第三个叶子节点输出,则表示为{0010}。将样本在每棵树的独热码拼接起来,表示为{1000010},即通过两棵CART树构造了7个特征,构造特征的规模与GBDT中CART树的叶子节点规模直接相关。

Wide&Deep在推荐中应用

【参考文献】

  1. He X, Pan J, Jin O, et al. Practical Lessons from Predicting Clicks on Ads at Facebook[C]. Proceedings of 20th ACM SIGKDD Conference on Knowledge Discovery and Data Mining. ACM, 2014: 1-9.

CSRF

​ CSRF(Cross-site request Forgery,跨站请求伪造)通过伪装成受信任用户请求受信任的网站。

1
注意:scrf漏洞并不需要获取用户的cookie等信息

目标:已经登陆了网站的用户

目的:以合法用户的身份来进行一些非法操作

需要条件:

​ 1.用户已经登陆了目标网站

​ 2.目标用户访问了攻击者构造的url

攻击过程:

​ 1.找到存于登陆状态的存在csrf网站的合法用户,向其发送可以构造的恶意链接,诱使其点击

​ 2.用户点击该链接,由该合法用户向服务器发出包含恶意链接里隐藏操作(如删除数据、转账等)的请求

​ 3.服务器收到已经登录用户的请求,认为是合法用户的主动的操作行为,执行该操作

典型的csrf实例

​ 当你使用网上银行进行转账时,首先需要登录网上银行,点击转账按钮后,会发出http://www.xxbank.com/pay.php?user=xx&money=100请求,当存在攻击者想要对你进行csrf攻击时,他会向你发送一个邮件或者短信,其中包含可以构造的恶意链接 http://www.bank.com/pay,php?user=hack&money=100,并且采用一定的伪装手段诱使你进行点击,当你点击后即向该hack转账100元。

流量中检测csrf的可行性

​ 1.对于比较低级的csrf而言,可以直接通过检测请求的referer字段来进行确定是否为scrf。因为在正常scrf页面中应该是在主页等页面跳转得到,而csrf请求一般的referer是空白或者是其他网站,但是该方法可以被绕过。

​ 2.完全的检测很难

csrf漏洞修复建议

​ 1.验证请求的referer

​ 2.在请求中加入随机的token等攻击者不能伪造的信息


SSRF

​ SSRF(Server-Side Request Forgery,服务端请求伪造)是一种有由攻击者构造请求,服务器端发起请求的安全漏洞。

目标:外网无法访问的服务器系统

目的:获取内网主机或者服务器的信息、读取敏感文件等

形成原因:服务器端提供了从其他服务器获取数据的功能,但没有对目标地址做限制和过滤

攻击过程:

​ 1.用户发现存在ssrf漏洞的服务器a的页面访问的url,以及可使用SSRF攻击的参数

​ 2.修改要请求参数要请求的文件,将其改成内网服务器b和文件,直接访问

​ 3.服务器a接收到要访问的参数所包含的服务器b和文件名,去服务器b下载资源

​ 3.对于服务器b,由于是服务器a发起的请求,直接将文件返回给服务器a

​ 4.服务器a将该文件或页面内容直接返回给用户

两种典型的ssrf攻击实例:

​ 本地存在ssrf漏洞的页面为:http://127.0.0.1/ssrf.php?url=http://127.0.0.1/2.php

原始页面的功能为通过GET方式获取url参数的值,然后显示在网页页面上。如果将url参数的值改为http://www.baidu.com ,这个页面则会出现百度页面内容。

​ 因此利用这个漏洞,我们可以将url参数的值设置为内网址,这样可以做到获取内网信息的效果。

探测内网某个服务器是否开启

​ 将url参数设置为url=”192.168.0.2:3306”时,可以获取大到该内网主机上是否存在mysql服务。

读取内网服务器文件

​ 访问ssrf.php?url=file:///C:/Windows/win.ini 即可读取本地文件

流量中检测SSRF可行性分析:

​ 对于只能抓到外网向内网访问的流量的网口来说,从流量中检测SSRF只能从请求参数异常返回包是否异常、是否包含敏感信息来进行检测。

SSRF漏洞修复建议:

​ 1.限制请求的端口只能是web端口,只允许访问http和https的请求

​ 2.限制不能访问内网IP,以防止对内网进行攻击

​ 3.屏蔽返回的信息详情

关键词提取算法


tf-idf(词频-逆文档频率)

tf计算公式

​ 其中count(w)为关键词出现的次数,|Di|为文档中所有词的数量。

tf计算公式

​ 其中,N为所有文档的总数,I(w,Di)表示文档Di是否包含该关键词,,包含则为1,不包含则为0,若词在所有文档中均未出现,则IDF公式中分母则为0,因此在分母上加1做平滑(smooth)

​ 最终关键词在文档中的tf-idf值:

tf计算公式

tf-idf特点:

1.一个词在一个文档中的频率越高,在其他文档中出现的次数越少,tf-idf值越大

2.tf-idf同时兼顾了词频和新鲜度,可以有效地过滤掉常见词


TextRank

​ TextRank算法借鉴于Google的PageRank算法,主要在考虑词的关键度主要考虑链接数量链接质量(链接到的词的重要度)两个因素。

​ TextRank算法应用到关键词抽取时连个关键点:1.词与词之间的关联没有权重(即不考虑词与词是否相似) 2.每个词并不是与文档中每个次都有链接关系而是只与一个特定窗口大小内词与才有关联关系。

TextRank特点:

​ 1.不需要使用语料库进行训练,由一篇文章就可以提取出关键词

​ 2.由于TextRank算法涉及到构建词图以及迭代计算,因此计算速度较慢

​ 3.虽然考虑了上下文关系,但是仍然将频繁次作为关键词

4.TextRank算法具有将一定的将关键词进行合并提取成关键短语的能力



在机器机器学习和深度学习中有许多常见的损失函数,主要包括:

​ 1.平方差函数MSE(Mean Squared Error)

​ 2.交叉熵函数(Cross Entory)

损失函数选择的方法:1.线性模型中使用平方误差函数,深度学习使用交叉熵函数

2.平方误差损失函数更适合输出为连续,并且最后一层不含Sigmoid或Softmax激活函数的神经网络;交叉熵损失函数更适合二分类或多分类的场景

线性模型

效果较好的损失函数:平方误差损失函数

计算公式:

​ 其中,y是我们期望的输出,a是神经元的实际输出a=σ(Wx+b)

损失函数求导:

​ 这也就是每次进行参数更新量的基数,需要再乘以学习速率

为什么深度学习中很少使用MSE作为损失函数?

​ 当使用MSE作为损失函数时,有上面求导后的公式可以明显的看出,每次的参数更新量取决于σ′(z) ,由Sigmod函数的性质可知,σ′(z) 在 z 取大部分值时会取到一个非常小的值,因此参数更新会异常的缓慢

深度学习

效果最好的损失函数:交叉熵函数

计算公式:

​ 如果有多个样本,则整个样本集的平均交叉熵为:

对于二分类而言,交叉损失函数为:

损失函数求导:

​ 对于b的求导同理。

​ 我们可以看出,交叉熵作为损失函数,梯度中的σ′(z) 被消掉了,另外σ(z)-y就是输出值和真实值之间的误差,误差越大,梯度更新越大,参数更新越快。

Softmax损失函数

softmax函数

​ softmax用于多分类过程中,将多个神经元的输出映射到(0,1)区间,可以看做被分为各个类的概率。

​ 其中,

softmax求导相关推导

​ 对于使用作为激活函数的神经网络,最终只输出只有最大的softmax最大的项为1其余项均为0,假设yj=1,带入交叉熵公式中得

​ 去掉了累加和,因为只有一项y为1,其余都为0,而将yj=1带入得

​ 下面我们准备将损失函数对参数求导,参数的形式在该例子中,总共分w41,w42,w43,w51,w52,w53,w61,w62,w63.这些,那么比如我要求出w41,w42,w43的偏导,就需要将Loss函数求偏导传到结点4,然后再利用链式法则继续求导即可,举个例子此时求w41的偏导为:

​ 其中右边第一项q求导为:

​ 右边第三项求导为:

​ 核心是求右侧第二项:$\frac{\partial a_j}{\partial z_j}$,这里我们分两种情况进行讨论

​ 将前两项的结果进行连乘得:

​ 而对于分类问题,只会有一个$y_i$为1,其余均为0,因此,对于分类问题:

​ 最终:

​ 在机器学习和深度学习中,选择合适的优化器不仅可以加快学习速度,而且可以避免在训练过程中困到的鞍点

1.Gradient Descent (GD)

​ BGD是一种使用全部训练集数据来计算损失函数的梯度来进行参数更新更新的方式,梯度更新计算公式如下:

1
2
3
for i in range(nb_epochs):
params_grad = evaluate_gradient(loss_function, data, params)
params = params - learning_rate * params_grad

缺点:

1.由于在每一次更新中都会对整个数据及计算梯度,因此计算起来非常慢,在大数据的情况下很难坐到实时更新。

2.Batch gradient descent 对于凸函数可以收敛到全局极小值,对于非凸函数可以收敛到局部极小值。

2.Stochastic Gradient Descent(SGD)

​ SGD是一种最常见的优化方法,这种方式每次只计算当前的样本的梯度,然后使用该梯度来对参数进行更新,其计算方法为:

SGD计算公式

1
2
3
4
5
for i in range(nb_epochs):
np.random.shuffle(data)
for example in data:
params_grad = evaluate_gradient(loss_function, example, params)
params = params - learning_rate * params_grad

​ 随机梯度下降是通过每个样本来迭代更新一次,如果样本量很大的情况,那么可能只用其中部分的样本,就已经将theta迭代到最优解了,对比上面的批量梯度下降,迭代一次需要用到十几万训练样本,一次迭代不可能最优,如果迭代10次的话就需要遍历训练样本10次。

缺点:1.存在比较严重的震荡

2.容易收敛到局部最优点,但有时也可能因为震荡的原因跳过局部最小值

3.Batch Gradient Descent (BGD)

​ BGD 每一次利用一小批样本,即 n 个样本进行计算,这样它可以降低参数更新时的方差,收敛更稳定另一方面可以充分地利用深度学习库中高度优化的矩阵操作来进行更有效的梯度计算

1
2
3
4
5
for i in range(nb_epochs):
np.random.shuffle(data)
for batch in get_batches(data, batch_size=50):
params_grad = evaluate_gradient(loss_function, batch, params)
params = params - learning_rate * params_grad

参数值设定:batch_szie一般在设置在50~256之间

缺点:1.不能保证很好的收敛性。

2.对所有参数进行更新时使用的是完全相同的learnnning rate

​ 这两个缺点也是前面这几种优化方式存在的共有缺陷,下面的优化方式主要就是为了晚上前面这些问题

4.Momentum

核心思想:用动量来进行加速

适用情况:善于处理稀疏数据

​ 为了克服 SGD 振荡比较严重的问题,Momentum 将物理中的动量概念引入到SGD 当中,通过积累之前的动量来替代梯度。即:

SGD计算公式

​ 其中,γ 表示动量大小,μ表示学习速率大小。

​ 相较于 SGD,Momentum 就相当于在从山坡上不停的向下走,当没有阻力的话,它的动量会越来越大,但是如果遇到了阻力,速度就会变小。也就是说,在训练的时候,在梯度方向不变的维度上,训练速度变快,梯度方向有所改变的维度上,更新速度变慢,这样就可以加快收敛并减小振荡。

超参数设定:一般 γ 取值 0.9 左右。

缺点:这种情况相当于小球从山上滚下来时是在盲目地沿着坡滚,如果它能具备一些先知,例如快要上坡时,就知道需要减速了的话,适应性会更好。

5.Adaptive gradient algorithm(Adagrad)

核心思想:对学习速率添加约束,前期加速训练,后期提前结束训练以避免震荡,减少了学习速率的手动调节

适用情况:这个算法可以对低频参数进行较大的更新,高频参数进行更小的更新,对稀疏数据表现良好,提高了SGD的鲁棒性,善于处理非平稳目标

​ 相较于 SGD,Adagrad 相当于对学习率多加了一个约束,即:

SGD计算公式

​ 对于经典的SGD:

​ 而对于Adagrad:

其中,r为梯度累积变量,r的初始值为0。ε为全局学习率,需要自己设置。δ为小常数,为了数值稳定大约设置为10-7

超参数设定:一般η选取0.01,ε一般设置为10-7

缺点:分母会不断积累,这样学习速率就会变得非常小

6.Adadelta

​ 超参数设置:p 0.9

​ Adadelta算法是基于Adagrad算法的改进算法,主要改进主要包括下面两点:

1.将分母从G换成了过去梯度平方的衰减的平均值

2.将初始学习速率换成了RMS[Δθ](梯度的均方根)

part one

​ (1) 将累计梯度信息从全部的历史信息变为当前时间窗口向前一个时间窗口内的累积

​ (2)将上述公式进行开方,作为每次迭代更新后的学习率衰减系数

其中 是为了防止分母为0加上的一个极小值。

​ 这里解决了梯度一直会下降到很小的值得问题。

part two

​ 将原始的学习速率换为参数值在前一时刻的RMS

​ 因为原始的学习速率已经换成了前一时刻的RMS值,因此,对于adadelta已经不需要选择初始的学习速率

7.RMSprop

​ RMSprop 与 Adadelta 的第一种形式相同:

使用的是指数加权平均,旨在消除梯度下降中的摆动,与Momentum的效果一样,某一维度的导数比较大,则指数加权平均就大,某一维度的导数比较小,则其指数加权平均就小,这样就保证了各维度导数都在一个量级,进而减少了摆动。允许使用一个更大的学习率η

超参数设置:建议设定 γ 为 0.9, 学习率 η 为 0.001

7.Adam

核心思想:结合了Momentum动量加速和Adagrad对学习速率的约束

适用情况:各种数据,前面两种优化器适合的数据Adam都更效果更好

​ Adam 是一个结合了 Momentum 与 Adagrad 的产物,它既考虑到了利用动量项来加速训练过程,又考虑到对于学习率的约束。利用梯度的一阶矩估计和二阶矩估计动态调整每个参数的学习率。Adam 的优点主要在于经过偏置校正后,每一次迭代学习率都有个确定范围,使得参数比较平稳。其公式为:

SGD计算公式

​ 其中:

SGD计算公式

SGD计算公式

总结:在实际工程中被广泛使用,但是也可看到在一些论文里存在着许多使用Adagrad、Momentum的,杜对于SGD由于其需要更多的训练时间和鞍点问题,因此在实际工程中很少使用

如何选择最优化算法

​ 1.如果数据是稀疏的,就是自适应系列的方法 Adam、Adagrad、Adadelta

​ 2.Adam 就是在 RMSprop 的基础上加了 bias-correction 和 momentum

​ 3.随着梯度变的稀疏,Adam 比 RMSprop 效果会好。

​ 整体来说Adam是最好的选择

参考文献:深度学习在美团点评推荐系统中的应用

https://blog.csdn.net/yukinoai/article/details/84198218

持久化

​ Spark中对于一个RDD执行多次算子的默认原理是这样的:每次你对一个RDD执行一个算子操作时,都会重新从源头处计算一遍,计算出那个RDD来,然后再对这个RDD执行你的算子操作。这种方式的性能是很差的。

​ 因此对于这种情况,我们的建议是:对多次使用的RDD进行持久化。此时Spark就会根据你的持久化策略,将RDD中的数据保存到内存或者磁盘中。以后每次对这个RDD进行算子操作时,都会直接从内存或磁盘中提取持久化的RDD数据,然后执行算子,而不会从源头处重新计算一遍这个RDD,再执行算子操作

​ spark中的持久化操作主要分为两种:persist和cachecache相当于使用MEMORY_ONLY级别的persist操作,而persist更灵活可以任意指定persist的级别。

如何选择一种最合适的持久化策略

  • 默认情况下,性能最高的当然是MEMORY_ONLY,但前提是你的内存必须足够足够大,可以绰绰有余地存放下整个RDD的所有数据。因为不进行序列化与反序列化操作,就避免了这部分的性能开销;对这个RDD的后续算子操作,都是基于纯内存中的数据的操作,不需要从磁盘文件中读取数据,性能也很高;而且不需要复制一份数据副本,并远程传送到其他节点上。但是这里必须要注意的是,在实际的生产环境中,恐怕能够直接用这种策略的场景还是有限的,如果RDD中数据比较多时(比如几十亿),直接用这种持久化级别,会导致JVM的OOM内存溢出异常
  • 如果使用MEMORY_ONLY级别时发生了内存溢出,那么建议尝试使用MEMORY_ONLY_SER级别。该级别会将RDD数据序列化后再保存在内存中,此时每个partition仅仅是一个字节数组而已,大大减少了对象数量,并降低了内存占用。这种级别比MEMORY_ONLY多出来的性能开销,主要就是序列化与反序列化的开销但是后续算子可以基于纯内存进行操作,因此性能总体还是比较高的。此外,可能发生的问题同上,如果RDD中的数据量过多的话,还是可能会导致OOM内存溢出的异常。
  • 如果纯内存的级别都无法使用,那么建议使用MEMORY_AND_DISK_SER策略,而不是MEMORY_AND_DISK策略。因为既然到了这一步,就说明RDD的数据量很大,内存无法完全放下。序列化后的数据比较少,可以节省内存和磁盘的空间开销。同时该策略会优先尽量尝试将数据缓存在内存中,内存缓存不下才会写入磁盘
  • 通常不建议使用DISK_ONLY和后缀为_2的级别:因为完全基于磁盘文件进行数据的读写,会导致性能急剧降低,有时还不如重新计算一次所有RDD。后缀为_2的级别,必须将所有数据都复制一份副本,并发送到其他节点上,数据复制以及网络传输会导致较大的性能开销,除非是要求作业的高可用性,否则不建议使用。

提高性能的算子

使用filter之后进行coalesce操作

通常对一个RDD执行filter算子过滤掉RDD中较多数据后(比如30%以上的数据),建议使用coalesce算子,手动减少RDD的partition数量,将RDD中的数据压缩到更少的partition中去。因为filter之后,RDD的每个partition中都会有很多数据被过滤掉,此时如果照常进行后续的计算,其实每个task处理的partition中的数据量并不是很多,有一点资源浪费,而且此时处理的task越多,可能速度反而越慢。因此用coalesce减少partition数量,将RDD中的数据压缩到更少的partition之后,只要使用更少的task即可处理完所有的partition。在某些场景下,对于性能的提升会有一定的帮助。

Shuffle

​ 大多数spark作业的性能主要就消耗在shuffle环节,因为shuffle中包含了大量的磁盘IO、序列化、网络数据传输等操作。但是影响一个spark作业性能的主要因素还是代码开发、资源参数、以及数据倾斜,shuffle调优在优化spark作业性能中只能起较小的作用。

shuffle操作速度慢的原因

Pyspark使用过程中的一些小Tips:

1、RDD.repartition(n)可以在最初对RDD进行分区操作,这个操作实际上是一个shuffle,可能比较耗时,但是如果之后的action比较多的话,可以减少下面操作的时间。其中的n值看cpu的个数,一般大于2倍cpu,小于1000。

2、Action不能够太多,每一次的action都会将以上的taskset划分一个job,这样当job增多,而其中task并不释放,会占用更多的内存,使得gc拉低效率。

3、在shuffle前面进行一个过滤,减少shuffle数据,并且过滤掉null值,以及空值

4、groupBy尽量通过reduceBy替代。reduceBy会在work节点做一次reduce,在整体进行reduce,相当于做了一次hadoop中的combine操作,而combine操作和reduceBy逻辑一致,这个groupBy不能保证。

5、做join的时候,尽量用小RDD去join大RDD.

6、避免collect的使用。因为collect如果数据集超大的时候,会通过各个work进行收集,io增多,拉低性能,因此当数据集很大时要save到HDFS。

7、RDD如果后面使用迭代,建议cache,但是一定要估计好数据的大小,避免比cache设定的内存还要大,如果大过内存就会删除之前存储的cache,可能导致计算错误,如果想要完全的存储可以使用persist(MEMORY_AND_DISK),因为cache就是persist(MEMORY_ONLY)。

8、设置spark.cleaner.ttl,定时清除task,因为job的原因可能会缓存很多执行过去的task,所以定时回收可能避免集中gc操作拉低性能。

逻辑回归模型


​ 逻辑回归算法是一种根据现有数据对分类边界线(Decision Boundary)建立回归公式,以此进行分类的模型。逻辑回归首先赋予每个特征相同的回归参数,然后使用梯度下降算法来不断优化各个回归参数,最终根据回归参数来对新样本进行进行预测。

注意:虽然名叫逻辑回归,但是实际上是一种分类模型

工作原理

1
2
3
4
5
每个回归系数初始化为 1
重复 R 次:
计算整个数据集的梯度
使用 步长 x 梯度 更新回归系数的向量(梯度下降)
返回回归系数

逻辑回归算法的特点

优点:计算代价低,可解释性强

缺点:容易欠拟合,分类精度可能不高

使用数据类型:数值型数据和标称型数据(只存在是和否两种结果的将数据)

sigmod函数

​ sigmod是一种近似的越阶函数,可以将任意的输入值,然后将其映射为0到1之间的值,其公式和函数图像如下图:

sigmod公式

sigmod函数

​ 在逻辑回归中先使用每个特征乘以一个回归系数,将其乘积作为sigmod函数中的z,即

逻辑回归中的z

​ 然后将其得到的值用sigmod函数映射到0到1,可以理解为被分为1类的概率。

梯度上升算法

​ 要找到某个函数的最大值,最好的方式就是沿着梯度方向不断地去寻找,如果梯度记做▽ ,则函数 f(x, y) 的梯度由下式表示:

sigmod函数

这个梯度意味着要沿 x 的方向移动 f(x, y)对x求偏导 ,沿 y 的方向移动 f(x, y)对y求偏导 。其中,函数f(x, y) 必须要在待计算的点上有定义并且可微。下图是一个具体的例子。梯度上升图

​ 上图展示了整个梯度上升的过程,梯度上升算法在到到每个点后都会从新估计移动的方向,而这个方向就是梯度方向,移动的速度大小由参数α控制。

训练过程

​ 训练算法:使用梯度上升寻找最佳参数

1
2
3
4
5
6
> 每个回归系数初始化为 1
> 重复 R 次:
> 计算整个数据集的梯度
> 使用 步长 x 梯度 更新回归系数的向量(梯度下降)
> 返回回归系数
>

​ 其中步长为超参数alpha,而梯度的计算如下:

梯度1

即每个点的数据和其输入数据相同。因此权重的更新可以使用:

w:=w+α error x

其中α为常数步长,error为在当前参数值下与目标值的误差经过sigmod函数处理后的值,x为当当前样本的输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import numpy as np

def sigmod(x):
return 1/1+np.exp(-x)

def gradAscend(dataSet,labelSet,alpha,maxCycles):

#将输入的数据转为向量格式
dataMat = np.mat(dataSet)
labelMat = np.mat(labelSet).tramsponse()
#获取输入数据的维度
m,n = np.shape(dataMat)
#初始化回归系数
weights = np.ones((n,1))
#对回归系数进行迭代更新

for i in range(maxCycles):
#计算使用当前回归系数LR的hx值,结果为(m,1)维向量
h = sigmod(dataMat*weights)
#计算误差
error = labelMat-h
#根据梯度进行回归系数更新
weights = weights + alpha*dataMat.transponse()*error
return weights

随机梯度上升算法

​ 随机梯度上升算法起到的作用和一般的梯度上升算法是一样的,只是由于一般的梯度上升算法在每次更新回归系数时需要遍历整个数据集,因此当数据量变动很大时,一般的梯度上升算法的时间消耗将会非常大,因此提出了每次只使用一个样本来进行参数更新的方式,随机梯度上升(下降)

随机梯度上升算法的特点:

​ 1.每次参数更新只使用一个样本,速度快

​ 2.可进行在线更新,是一个在线学习算法(也是由于每次回归系数更新只使用一个样本)

工作原理:

1
2
3
4
5
所有回归系数初始化为 1
对数据集中每个样本
计算该样本的梯度
使用 alpha x gradient 更新回归系数值
返回回归系数值

初步随机梯度下降代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def stocgradAscend(dataSet,labelSet):
#1.这里没有转换成矩阵的过程,整个过程完全都是在Numpy数据完成的
alpha = 0.01

m,n = np.shape(dataSet)

weights = np.ones((n,1))
#2.回归系数更新过程中的h、error都是单个值,而在一般梯度上升算法中使用的是矩阵操作
for i in range(m):
h = np.sigmod(dataSet[i]*weights)
error = h - labelSet[i]
weights = weights + alpha*error*dataSet[i]

return weights

但是这种随机梯度上升算法在在实际的使用过程出现了参数最后难以收敛,最终结果周期性波动的问题,针对这种问题我们对这个问题将随机梯度下降做了下面两种优化

​ 1.改进为 alpha 的值,alpha 在每次迭代的时候都会调整。另外,虽然 alpha 会随着迭代次数不断减少,但永远不会减小到 0,因为我们在计算公式中添加了一个常数项。

​ 2.修改randomIndex的值,从以前顺序的选择样本更改为完全随机的方式来选择用于回归系数的样本,每次随机从列表中选出一个值,然后从列表中删掉该值(再进行下一次迭代)。

最终版随机梯度下降:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
def stocgradAscend(dataSet,labelSet,numsIter=150):

m,n = np.shape(dataSet)
weights = np.ones(n,1)
alpha = 0.01

for i in range(numsIter):
#生成数据的索引
dataIndex = range(m)
for i in range(m):
#alpha会随着i和j的增大不断减小
alpha = 4/(i+j+1.0)+0.001 # alpha 会随着迭代不断减小,但永远不会减小到0,因为后边还有一个常数项0.0001
#生成随机选择要进行回归系数更新的数据索引号
randomIndex = np.random.uniform(0,len(dataIndex))
h = sigmod(np.sum(dataSet[dataIndex[randomIndex]]*weights))
error = h - dataSet[dataIndex[randomIndex]]*weights
weights = weights + alpha*error*dataSet[dataIndex[randomIndex]]
#在数据索引中删除
del(dataIndex[randomIndex])
return weights

预测过程

​ LR模型的预测过程很简单,只需要根据训练过程训练出的参数,计算sigmod(w*x),如果这个值大于0.5,则分为1,反之则为0

1
2
3
4
5
6
def classfyLR:(inX,weights)
prob = sigmod(np.sum(weights*inX))
if prob>=0.5
return 1
else:
return 0

注:这里的阈值其实是可以自行设定的

一些其他相关问题


1.LR模型和最大熵模型

​ (1).logistic回归模型和最大熵模型都属于对数线性模型

​ (2).当最大熵模型进行二分类时,最大熵模型就是逻辑回归模型

​ (3) 学习他们的模型一般采用极大似估计或正则化的极大似然估计

​ (4)二者可以形式化为无约束条件下的最优化问题

2.LR模型的多分类

​ 逻辑回归也可以作用于多分类问题,对于多分类问题,处理思路如下:将多分类问题看做多个二分类,然后在各个sigmod得到的分数中区最大的值对应的类作为最终预测标签。

​ 概率图模型是用图来表示变量概率依赖关系的理论,结合概率论与图论的知识,利用图来表示与模型有关的变量的联合概率分布

​ 基本概率图模型主要包括贝叶斯网络马尔科夫网络隐马尔科夫网络三种类型。

​ 基本的Graphical Model 可以大致分为两个类别:贝叶斯网络(Bayesian Network)和马尔可夫随机场(Markov Random Field)。它们的主要区别在于采用不同类型的图来表达变量之间的关系:贝叶斯网络采用有向无环图(Directed Acyclic Graph)来表达因果关系,马尔可夫随机场则采用无向图(Undirected Graph)来表达变量间的相互作用。这种结构上的区别导致了它们在建模和推断方面的一系列微妙的差异。一般来说,贝叶斯网络中每一个节点都对应于一个先验概率分布或者条件概率分布,因此整体的联合分布可以直接分解为所有单个节点所对应的分布的乘积。而对于马尔可夫场,由于变量之间没有明确的因果关系,它的联合概率分布通常会表达为一系列势函数(potential function)的乘积。通常情况下,这些乘积的积分并不等于1,因此,还要对其进行归一化才能形成一个有效的概率分布——这一点往往在实际应用中给参数估计造成非常大的困难。

概率图模型的表示理论

​ 概率图模型的表示由参数和结构两部分组成。根据边有无方向性,可分为下面三种:

a、有向图模型—贝叶斯网络

b、无向图模型—马尔科夫网络

c、局部有向模型—条件随机场和链图

一、概述

最优化问题主要分为

​ 1.无约束优化问题

​ 2.等式优化问题

​ 3.含不等式优化问题

对于无约束问题常常使用的方法就是Fermat定理,即求取f(x)的倒数然后另其为0,可求得候选最优质,如果函数为凸函数则直接为最优值。

​ 在求取有约束条件的优化问题时,拉格朗日乘子法(Lagrange Multiplier) 和KKT条件是非常重要的两个求取方法。二者各有其使用范围:

拉格朗日乘子法:等式约束的最优化问题

KTT条件:不等式约束下的最优化问题

对于一般的任意问题而言,这两种方法求得的解是使一组解成为最优解的必要条件,只有当原问题是凸问题的时候,求是求得的解是最优解的充分条件

二、有约束最优化问题的求解

1.拉格朗日乘子法

​ 对于等式约束,我们可以通过一个拉格朗日系数a 把等式约束和目标函数组合成为一个式子L(a, x) = f(x) + a*h(x), 这里把a和h(x)视为向量形式,a是横向量,h(x)为列向量,然后求取最优值,可以通过对L(a,x)对各个参数求导取零,联立等式进行求取。

2.KTT条件

​ 对于含有不等式的约束条件最优化问题,我们可以把所有的不等式约束、等式约束和目标函数全部写为一个式子L(a, b, x)= f(x) + ag(x)+bh(x),KKT条件是说最优值必须满足以下条件:

  1. L(a, b, x)对x求导为零;

  2. h(x) =0;

  3. a*g(x) = 0;(g(x)为不等式约束)

求取这三个等式之后就能得到候选最优值。