0%

本篇单独开出来,主要将对自己做过的一些个人项目近些一些总结,希望能在反思中不断进步。
所以,本文也会持续的更新。。。。。。

Home Credit Deafult Risk

Private Leaderboard 77/7198
本项目来自Kaggle竞赛,其实蛮早就关注平台了,但是原来自己水平及装备有所不及,所以多是看看。

本项目可以算得上是对我来说第一个真正意义上的竞赛。从一开始发布便跟上大神们的更新脚步,因有希望能够保持在前列,使我有了更多的动力参与,为此,甚至提前购置了MBP。

说实在的,在之前,对分类、回归及各种模型,多是有一些了解,那时对特征工程虽然也有听闻,但是具体应该怎么深入操作,其实是有一点迷糊的。经过比赛的洗礼,从数据的准备,特征处理,模型训练,结果评估,循环迭代,都有了一些操作能力。能够在一些工程的角度来考虑一些问题了。

Kaggle的分享精神真是让我学到蛮多的,将来我也会回馈社区的。

紧跟大神的脚步就能飞快的成长。

当然具体做的时候还是需要有一些想法,让代码和数据来验证想法,如何快速实现这样的迭代其实就是实现能力来,非常重要。

整个比赛历时3个月,由于那段时间手上没有什么工作,基本都在做这件事。具体到一些特征的处理其实跟业务的相关性比较高了,如果不是在这个业务领域,恐难深入理解。

本来比赛结束就想把这个总结写了,但是那时换工作了,也有一堆事情需要处理,所以就一直拖到了现在,想着要是再不写可能就凉了。技术上的一些探索和学习主要在于模型融合、特征工程。
特征工程主要包括特征之间的关系,以及使用NN模型或者遗传算法的输出作为输入。其中遗传算法生成的特征在线下的表现非常好,但是到线上的表现却很差,也做过很多次的检查,至今仍然疑惑。NN模型的特征探索是积极的,top方案也有比较好的反馈,但当时,对NN还不是十分熟悉,所以,也只是依葫芦画瓢,没有什么很好的结果。另外,对于分类任务来说,特征工程最主要的目标在于能够发现比较好的表征使得目标可以很好的区隔开来,当然,理论上来说最好的特征是与目标值相关(线性或者非线性)的值,我们的工作就是去找这个可能(也可能没有)的关系将特征表征出来。

其它学到的东西估计就是对于整个流程的熟悉了。

由于后面将开启一个新的project,所以关于这个项目的数据将暂时移出电脑,本来还想将top方案跑一跑学习一下的,现在可能只会去看看代码了。

Elo Merchant Category Recommendation

Private Leaderboard 137/4221

1-连续数值的最后stack,建议使用BayesianRidge操作,并尽量不在后续再叠加人工权重与其它模型集成.

1
from sklearn.linear_model import BayesianRidge

2-NN用得好几乎可以达到lgb同等的效果,不过要看数据集/模型/参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
def ann(input_shape):
“”“
just for this project maybe helpful.
”“”
model = Sequential()
model.add(Dense(2 ** 10, input_dim = input_shape, init='random_uniform', activation='relu'))
model.add(Dropout(0.25))
model.add(BatchNormalization())
model.add(Dense(2 ** 9, init='random_uniform', activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.25))
model.add(Dense(2 ** 5, init='random_uniform', activation='relu'))
model.add(BatchNormalization())
model.add(Dropout(0.25))
model.add(Dense(1))
model.compile(loss='mean_squared_error', optimizer='adam')
return model

学习小笔记

情感分析

因为没有历史情感数据进行建模,无法导入机器学习算法形成模型。
所以利用了情感倾向词表进行规则计算。规则是:否定词+程度副词+情感词
其中每个部分都需要维护一个基础数据表,在新句子中搜索组成此结构,然后计算每个结构的情感得分。
一般来讲传统方法会对整个句子计算整体的情感得分,就是将所有的最小单元求和。
我做了一点改进的是:将每个最小单元的各项值保留,形成句子的$3xN$情感矩阵,这样做的好处是不仅可以计算整个句子的得分,还可以计算每个单元的得分。这样将有可能计算句子的局部情感信息,以及局部信息与全部信息的关系,势必将可以捕捉到更多的特征。例如:某个句子的一个极度负面的用词,对于整体长句是否应该一票否决?不知道,或许不同场景不一样,但特征捕捉出来,然后进行实验就可以知道了。

tfidf

统计词频并用全局词频进行降权。

word2vec

生成方法还没有去做具体研究。
理解是基于语料库的词组搭配关系自动生成词语向量量用于刻画语义,可以刻画词语间的相似性。
在后续的使用上有一点小思考,看到的应用都是将句子的所有的词语的向量求和,然后计算句子间的相似性,这样的话就会是两个向量求余弦的操作。
这样固然简单,但我有一个一个疑问,这样不就将顺序所包含的信息给丢掉了吗?
难道不能够用词语向量所组成的矩阵计算相似性?难点在哪里?还在思考。。。
可能是$MxN$与$KxL$矩阵的相似性计算在数学上不好操作?有没有近似操作?

训练词向量的两种方式:
Skip-gram
CBOW
训练词向量的核心在于使用贝叶斯推理公式

word2vec线上部署思路

初步的一些思路是:
本地load,加载到内存中,当然如果每次调用都需要load的话,必定很慢.这也就对应用服务器的内存提出更高要求,一般word2vec至少有5G-10G大小.
调用REST API,为请求词向量单独起个服务,感觉这个架构会比较清晰简单,但是如果需要使用到所有的词向量的话也会比较麻烦
加载到本地数据库中,即时调用。Sqlite或许是个不错的选择,但是由于Sqlite的容错性没有做检查,所以并发比较高的情况下在不同的操作系统下可能会有问题.
文件映射,或许可以参考一下annoy.C++写的一个框架,单次调用时间在20us左右,也不暂用内存.

word2vec 与 fasttext

这篇文章讲得很好,可以参考:Word2Vec and FastText Word Embedding with Gensim
讲得简洁清晰。

对于英文来说,使用fasttext可以训练的w2v可以识别到未出现过的词,因为他会考虑子元组,词未在训练集中出现过,子元组却极有可能是出现过的。

基于词向量的主题模型

基于词袋的LDA主题模型,无法考虑词语顺序、词意,仅能表示相同词语在不同句子中的存在与否、存在频率关系。所以在对问题进行主题分类的任务中应该不会有很好的表现,在实际测试中也确实表现不如人意。

可以基于词向量对模型进行修改,进行主题建模的根本还是衡量句子间距离(在整个句子空间上),将所有句子分成若干类。能将句子有效表示成向量,就变成了经典的聚类问题。
在基于词向量表示句子向量时,使用了tfidf对各词语进行了加权平均得到句子向量表示,然后再进一步将从句子中得到关键词(一个)的向量加到加权平均句子向量上。(这一步相当于再次加权)。得到最终句子平均向量。

对得到向量表示句子进行无监督聚类,效果不错。

精准率与召回率

看到的很多文章好像都挺喜欢用正类反类来作为例子进行公式说明。
但我在理解之后,觉得这样好像不是十分妥当。
一是,针对我的业务我可能需要去理解什么是正类,什么是反类,不是label为0就是反类吧。
二是,基于一,就有了问题,何为正?何为反?正反的定义对于指标的理解是否有意义或是帮助?我认为,正反的定义是不必要的。

所以,我觉得。直接使用(某类-另一类)或(某类-[其它类])可以更好的理解指标意义。
总而言之,精准率与召回率指标的定义与所谓正反是无关的,它是基于某类的。
所以:
精准率:某类预测量中实际正确所占比例
召回率:某类实际量中预测正确所占比例

使用某类则可以直接扩展到多类的场景而不需要做什么多余的改变。每个类别都会有自己的精准率与召回率,所以使用某类。
例子就贴一张sklearn.metrics计算的几个指标值以及混淆矩阵,可以手动带入公式看看具体是怎么算的,有助于理解。Markdown

classification report中的avg是使用加权平均计算的,权为样本数量。

合并两个有序数组

思路1:最简单的操作是将nums2直接拼在nums1后面,然后对nums1排序
这样的话会用到python自带的排序算法,也可以自己写一下快排代替。总体来讲是先合并再排序。这样的话就没有使用到给出的有序数组中有序的信息。所以复杂度难免会高一点。

1
2
3
4
def merge(nums1, m, nums2, n):
nums1[m:] = nums2[:n]
nums1.sort()
return nums1

思路2:利用有序信息,依次比较插入nums1中,这种复杂度会低一些,但是边界的判断会复杂一点。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
def merge(nums1, m, nums2, n):
"""
:type nums1: List[int]
:type m: int
:type nums2: List[int]
:type n: int
:rtype: void Do not return anything, modify nums1 in-place instead.
"""
i = m+n-1 # nums1的下标长度
while(i >= 0):
if (len(nums1)==0) or (len(nums2)==0): # 如果有一个数组为空,则直接拼接
nums1[m:] = nums2
elif (m == 0): # 如果nums1已经比较完了,则将nums2的剩余部分赋值到nums1
nums1[i] = nums2[n-1]
n = n - 1
elif (nums1[m-1] >= nums2[n-1]): # 否则比较两数组最后一个元素大小,将大者放入nums1后,并更新下标
nums1[i] = nums1[m-1]
m = m-1
elif (nums1[m-1] < nums2[n-1]) and (n-1>=0): # 判断大小并检查nums2是否判断完毕,如果nums2判断完毕,则不再利用nums中值,避免循环引用
nums1[i] = nums2[n-1]
n = n-1
i = i - 1
return nums1