No Activity tracked this Week
- 🔭 Working with the Julia programming language.
在数据挖掘实战中锻炼处理实际数据特征的能力
(近期DataFountain和CCF搞的大数据比赛里,我选题之一是搜狗的一道题。此处链接放上:)
感觉自己一直有点晕乎,这题属于文本挖掘,但我选这题的时候还对相关方面所知甚少,最终虽然大有进步,但是还是没进复赛。另一道题倒是第一次提交就排进前五十,真是唏嘘不已。
简单介绍一下题目:
<引用>
在现代广告投放系统中,多层级成体系的用户画像构建算法是实现精准广告投放的基础技术之一。其中,基于人口属性的广告定向技术是普遍适用于品牌展示广告和精准竞价广告的关键性技术。人口属性包括自然人的性别、年龄、学历等基本属性。
在搜索竞价广告系统中,用户通过在搜索引擎输入具体的查询词来获取相关信息。因此,用户的历史查询词与用户的基本属性及潜在需求有密切的关系。
举例如下:
1、 年龄在19岁至23岁区间的自然人会有较多的搜索行为与大学生活、社交等主题有关
2、 男性相比女性会在军事、汽车等主题有更多的搜索行为
3、 高学历人群会更加倾向于获取社会、经济等主题的信息
本题目提供用户历史一个月的查询词与用户的人口属性标签(包括性别、年龄、学历)做为训练数据,要求参赛人员通过机器学习、数据挖掘技术构建分类算法来对新增用户的人口属性进行判定
</引用>
数据格式如下:
<引用>
ID 加密后的ID
age 0:未知年龄; 1:0-18岁; 2:19-23岁; 3:24-30岁; 4:31-40岁; 5:41-50岁; 6: 51-999岁
Gender 0:未知1:男性2:女性
Education 0:未知学历; 1:博士; 2:硕士; 3:大学生; 4:高中; 5:初中; 6:小学
Query List 搜索词列表
数据示例:
对于train.csv中的数据记录:
00627779E16E7C09B975B2CE13C088CB 4 2 0 钢琴曲欣赏100首 一个月的宝宝眼睫毛那么是黄色 宝宝右眼有眼屎 小儿抽搐怎么办 剖腹产后刀口上有线头 属羊和属鸡的配吗
</引用>
简单来说,就是要你通过每个用户的搜索记录来对其年龄、性别和学历进行分类(以便搜狗做更好的推荐服务)。
之前在学校搞建模的时候做过一点点相关的,但之前那种属于toy datasets,基本怎么都能有高精度,但这个题的数据虽说不大,好歹也有40多MB的文本,肯定不是瞎跑就成的。
文本挖掘是真正的高维特征处理,仅仅针对“高维”二字,你就需要在如下事情上死命纠缠:
1.多快好省地处理稀疏矩阵。
2.利用多种方法,有效地进行降维,或者清理掉质量不好的特征。
3.寻找有效的文本特征提取办法。
在有些机器学习问题中,凭借自己的直觉去处理数据特征,然后手工产生含有高维信息的低维数据,会有非常好的结果。这在Kaggle上是很常见的————大神们用一些奇葩的想
法,搞出一些感觉很有道理的特征,然后get a higher score,让你一脸懵逼。但是这里不行,你需要来一点数学。
我先说一下我的做法吧。
首先是特征的提取问题。
首先的首先是分词,词语在文本分析中以一种基本语义单位存在着,当然,也会有用单字符做单位的情况。
文本基本是中文的,分词的话,使用比较成熟的分词工具即可,我选择的是jieba分词。
分词过后就需要基于词语建立数据特征。
我尝试过提取词向量和文本向量特征,不过也许由于没有找到其他的语料库,仅仅利用train+test数据集里文本做语料,效果非常之差。
之后,虽然心怀忐忑,还是对所有出现的词做OneHotEncoding,也就是哑编码,效果稍微好一点。
对于当时完全不了解文本特征提取的我来说,几乎已经做不动了。当时我的score只有0.54左右。
查阅了一些资料,找到了TF-IDF这个方法,词率-逆文档频率。一般来说,这个算法是这样做的:对于一类文档doc,计算出某个词w在doc中出现的频率f1(w的数目/文章总词数),再计算这个词在所有文档类
中出现的频率f2(在所有文档类集合docs减去doc后得到docs2中,总文档类数/出现w的文档类数)。那么我们注意到,f1越高,说明doc与w相关性大,但反之不一定成立,因为可能有这样一种情况:docs中文档类均
含有高频率出现的w,这样的话,w并不能作为doc特有的一个标志;但如果f2越高,则doc外的其余文档类出现w的频率越低,在f1较大的情况下,就能够说明doc和w之间互相有依赖关系,这样一来,w就能作为区分
可能性越低,则说明w与其他文档类关系越小,w是一个区分文档类doc的好的特征词。
当然,TF-IDF的计算公式,一般是fw_i*log( R_not_i / ( 1 + Rw_not_i ) ) ,其中fw_i是w在i文档类出现的频率,R_not_i是文档类i之外的文档类总数,R_i则是w在文档类i之外出现w的文档频次总数。
你问+1是为了什么,我就告诉你这是为了续一秒。你问log一下是为什么,我们总不能让数值太大是不是————我们要的只是数值上的区分度,同时这对于某些分类器(比如SVM)来说,也是重要的数据预处理。
利用TF-IDF,我倒是成功把score提升到了0.6。
当然,TF-IDF自然还不是终点。
还有一些很重要的特征提取方法,比如哈希技巧,我倒不是很懂它的算法,不过它可以极其快速地产生大量的文本向量特征。
以上的方法,TF-IDF和OneHotEncoding以及哈希技巧结合起来,对于分词后的文本能产生至少50W维的特征,你直接用是可以的,但是,这么大特征,肯定只能用一下
SGD、RandomForest这种随机抽数据特征以及数据条的算法,很难说把这么大的数据利用好。
这个时候数学就该来了。SVD算法当然很好很强大,但是,几十万的稀疏矩阵,我看你机器跑一下.toarray()好吧?据我最新的导师说,SVD可以做并行,但是我没找到,自己
要写并行SVD感觉毫无思路,所以直接齐了。
PCA也不行,这个可能是我机器问题,4G内存太渣。也可能是算法问题,我记得我用过是numpy还是sklearn的pca,内存瞬间爆炸。
最后我用的卡方检验,效果比较好,直接可以降到3、4万维,经过测试这也大概是我的分类器表现最好的特征数范围。
我倒是有一点其他的思路,借用代数中线性无关的概念,利用机器学习方法,选定一个特征作为target,训练其他特征对它进行预测,如果MCC和ACC都比较高,那么就可以说这个特征可以由其他特征表出,
将机器学习确定的模型看做一个泛函,我们就说在这个泛函下,这个特征可以由其他特征表出,即与其他特征相关。这意味着我们可以尝试去掉它。当然这并不是很合理,因为像Kaggle上那些大神手工总结出
的包含高维信息的特征,虽然可能与其他特征都有不小的相关性,但去掉它却是不太合理的————这说明了正交化并不是特征选取的根本宗旨。
不过,根据信息量(准确地说是信息增益)来选特征肯定没错————入门决策树算法的时候,有个经典的东西,熵,以及它的衍生物gini不纯度等等。我们可以根据熵来判断某个特征里是否包含较多的信息,
从而判断出该特征对于分类的效果。
特征处理大概就说到这里,不知道这些时觉得好难好难,现在一看蠢得一逼。
还有就是分类器选择以及调参的问题。
这次比赛,我算是践行一个原则:绝不人为调参,绝不玄学。虽然似乎因此付出了代价。
因为数据量太大,我使用的分类器就是LogisticRegression、SGD和随机森林,参数的话只是把树的数量设置到100(这应该不算调参的吧?)
不过我使用了分类器集成的办法,用一个VotingClassifier集成不同数量的LR、SGD、RandomForest,效果比单分类器要好那么一丁点。事实上由于计算机内存的原因,最终我的集成分类器不过就是由占大部
分的SGD和少部分的LR。听到榜前的很多大神说,用SVM跑的效果最好,但是我跑SVM出来做交叉验证似乎也不是很好,而且最重要的是我的电脑跑一次数据是3W维稀疏矩阵的
SVM,大概就要花2个小时,开多线程的话内存又吃不消,实在是没有时间去探索SVM的奥妙。
最后,上一点代码吧。暂时还是Sklearn党+Pybrain党,有点惭愧,希望不久能自己写出一个造福社会的开源包。
特征处理:
import scipy.sparse as sp
from sklearn.externals import joblib as jb
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.feature_extraction.text import HashingVectorizer
tar=jb.load('/home/thaut/doc/df/time2__/train_target') //这个是已经处理过的target数据
words=jb.load(r"/home/thaut/doc/df/time2__/all_words") //words是一个列表,其中每个元素是,将每个用户的搜索记录分词后用' '连接形成的字符串。
hv = HashingVectorizer(n_features=350000,non_negative=True,norm='l2') // non_negative主要是为了后面卡方特征选择(不能有负数)时做的预处理
tfidf1=TfidfVectorizer(analyzer='word',stop_words=stopws) //stopws是我自己找的并处理过的一个停用词库,这里的类型是一个set
data=tfidf1.fit_transform(words) //这个地方的处理是有问题的。不过在博客上我就写个样子,实际上,应该将文档归类,然后针对每种标签(比方年龄就是一种标签)训练一套数据。
ds=hv.fit_transform(words)
ds=sp.hstack((ds1,data),format='csr') //稀疏矩阵大法好啊
jb.dump(ds,'data_all')
分类器:
import pandas as pd
from sklearn.externals import joblib as jb
from sklearn.naive_bayes import MultinomialNB as nb
from sklearn.feature_selection import SelectKBest,chi2
from sklearn.linear_model import SGDClassifier as SGD
from sklearn.neighbors import KNeighborsClassifier as KNN
from sklearn.ensemble import VotingClassifier as votc
from sklearn.ensemble import RandomForestClassifier as rf
from sklearn.svm import SVC
from sklearn.grid_search import GridSearchCV as CV
from sklearn.cross_validation import train_test_split as tts
from sklearn.linear_model import LogisticRegression as LR
from sklearn.ensemble import GradientBoostingRegressor as GBR
def c(s):
if s=='SGD':
return SGD(penalty='l2')
elif s=='nb':
return nb()
elif s=='LR':
return LR()
elif s=='rf': //间歇性炸内存
return rf(n_estimators=50)
elif s=='SVC': //跑到天荒地老
return SVC(kernel='linear')
elif s=='GBR':
return GBR() //跑太久
elif s=='KNN':
return KNN(n_neighbors=3) //其实KNN用不了,用炸内存,KD树没试
def mixclass(alist): //简单集成
clf=[]
vot=None
for i in alist:
for s in range(i[1]):
sc=c(i[0])
clf.append(sc)
num=len(clf)
numarr=list(range(num))
classifier=list(zip(numarr,clf))
vot=votc(estimators=classifier,voting='hard',n_jobs=1+int(num/10))
return vot
KBest=SelectKBest(chi2,k=30500)
X_new=KBest.fit_transform(data[:19982],tar[i]) //前19982是训练集数据,tar是一个列表,其中每个元素是训练集上一种target的情况
话说,写博客时才发现自己有很多细节出了问题。所以说有些事情,总是一个人做,可能不是很合适。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.