01机器学习入门教程,启发式实例(分析/开发/算法新手/有志从事数据方向的毕业朋友入,高手绕)...

作者·黄崇远

『数据虫巢』

全文共13000

题图ssyer.com

 仅适合有志于学习一点算法的数据开发或者数据分析,以及对于算法感兴趣的新手朋友们,又或者一些刚毕业或者即将毕业想进入数据行业者,此外,高手可以绕道。

这是知识星球的第一篇辅导式的入门实例,核心是了解机器学习的基本过程,并且这只是一个启发式的实例,把最基本的流程走完,拿到结果。

写在前面。

对于新手朋友们来说,如果真的想要了解一些机器学习的东西,甚至后面接触推荐系统和甚至是广告相关的深层的机器学习应用场景,那么动起手来是最直接的学习方式。

对于新手朋友们来说,可能是搭建一个基础的支撑你练习的环境都很困难,但如果一步一步来克服这些困难,实际自己动手把这个最最最简单的CASE跑起来,最起码是离你知道算法是什么东西又会近一点点,虽然这一点点只是非常少的一点。

这里只做整理,不做任何的讲解和答疑以及辅导,如果想有相应的学习环境或者氛围,以及相关知识的交流沟通,可以加入知识星球(文末有二维码)。

01

实例目标

  • 结合场景了解基本的机器学习过程。

  • 结合实例讲解实际产生过程的一些事项。

  • 了解基本的机器学习算法。

  • 了解基本的Python的使用过程。

实际上最大的目的在于给大家一个联系的入口,接触机器学习的基本知识,如果你想了解最基本的机器学习相关内容,以及做相关的事。

后面会围绕这个数据集,做python的基本用法、机器学习库的使用、模型的使用、模型调优,数据复杂处理、特征选择、模型评估等进一步的学习。

再次强调,跑通这个最基本的例子是开始。

02

基本的知识准备

  • 基本的Python语法以及Pandas库的使用。

    (包括对pandas的使用,中文教程:https://www.pypandas.cn/ )

  • Python的Jupyter NoteBook编辑器使用。

    (先谷歌“anaconda3安装”,然后百度基于“anaconda安装jupyter”,非常简单)

  • XGBoost算法的基本理论。

    (可以参考这篇理论知识:https://zhuanlan.zhihu.com/p/90520307)

在做实际的实例跑之前,你需要一个能够跑python3的环境,以及对pandas基本了解,然后一个python的调试编辑环境jupyter,最后对这个实例中涉及到sklearn库和XGB库有个基本了解,或者说不了解也行,最起码先把这个实例跑起来,再来深入了解原理。

03

什么是CTR和Jupyter

首先,这是一个最基本的基于CTR的实例,然后模型层使用XGB做支持。

本实例的主体部分我们将用Kaggle开源的数据集,然后用XGBoost算法,做一个简单的CTR模型,这是代码部分。然后基于这个实例,我们会讲解在实际生产工作中的差异部分,即比如生产环境中数据实际上是怎么来的,在实际生产环境中,我们怎么部署模型等等,完善这个实例的其他部分。

至于为什么用Xgboost这个算法库,是因为这个算法库目前从传统学习的角度来说,效果非常好和稳定,并且在很多实际的生产环境中,依然会使用他,是一个非常实用的算法模型。

CTR的全称是点击率,即我们通过模型来预估用户对于某个事务的点击概率,在非常多的场景中有所应用,比如在广告逻辑中,我们需要预估用户对于广告的点击率,然后根据点击率的高低来决定给当前用户推送什么广告。

在推荐系统逻辑中,我们需要对当前用户计算候选(就是待推荐商品或者其他东西)物品的点击率,然后把点击率高的商品或者物品或者新闻之类的推送给他。

总之,你可以在任何类似需要给用户推荐东西的场景里,认为这是一种匹配的依据。因为我们推荐东西给用户,最终是想让用户点击他,所以当模型预估出来某个物品点击率高的时候,这意味着用户点击他的概率可能就大些。

所以,CTR预估是一个非常重要的基本算法场景。

Python的Jupyter编译环境,支持逐行的编译,以及具有良好的调试方式支持,所以非常适合做一些算法模型的初始实验建模,数据理解,以及快速探测模型效果,评估效果。

我们在实际的工作过程中,也经常会用到他,用来做快速的模型调试,数据理解和特征探索之类的前期工作,等所有都明确下来之后,才会做正式的工程化。

所以,这里我们用来做实例教程太好不过了。

所以,我们的演示都会在jupyter下进行。

04

数据来源以及特征处理

  • 基本的数据集:https://www.kaggle.com/c/avazu-ctr-prediction

这是一个非常有名的CTR预估的开源数据集,当年的CTR预估竞赛非常有名,并且这个数据集是实际的企业数据集,只不过脱敏了。但这个数据集非常大,光原始数据就大几个G,正常情况下我们的小服务器做练习的话,根本处理不过来了。

所以,我们可以在这上面随便截取部分来做实例演练。

  • 截取之后的数据集:https://www.kaggle.com/sulabh4/ctr-prediction-dataset

这个数据集其实就是上面那个的子集,只不过是别人截断的,也可以自己截断,总共100万条,共150MB,正常情况下我们自己的小笔记本应该是可以加载处理了。

数据我已经放在了网盘上。

#读入数据,并查看数据的正负样本比例(click=1为历史点击的用户,0为非点击用户,通过点击与否区分正负样本)。这里最重要的import的python包为pandas,就是之前说的处理数据的包,并且绝大部分python的机器学习库都接受pandas对应的dataframe数据格式。

import warnings

warnings.filterwarnings("ignore")

import pandas as pd

#我们把数据放在新建的当前data目录下,通过pandas的read_csv读入csv格式的数据。

file = './data/train_subset_1000000.csv'

df = pd.read_csv(file)

#然后打印样本的数量和正负比例,其中筛选dataframe的指定字段,可以用df[df["click"] == 1]类似这种方式来过滤,即click字段=1的数据。也可以用.id直接取id这个字段,用count()来统计记录数。

print(f'--All data:{df.id.count()}')

y_1_nums = df[df["click"] == 1].id.count()

y_0_nums = df[df["click"] == 0].id.count()

print(f'--1 data:{y_1_nums}')

print(f'--0 data:{y_0_nums}')

print(f'--0 VS 1 => {round(y_0_nums/y_1_nums,2)}:1')

#这是输出,可以看到总共999999条数据,之前说的100万是包括表头,负正比例为5.24:1。

--All data:999999

--1 data:160219

--0 data:839780

--0 VS 1 => 5.24:1

#我们使用dataframe.info来观测一下所有特征。

df.info()

#结果如下,我们可以看到所有的字段,id是用户的唯一id,click是是否点击的标志,1=正样本,0=负样本。剩下有些是int类型,有些是obeject类型,int类型可以直接用于模型,object类型的,我们需要转换成int或者float类型才行。

#从字段的命名上,有些字段是加密过的,有些是从字面上可以理解的字段。

<class 'pandas.core.frame.DataFrame'>

RangeIndex: 999999 entries, 0 to 999998

Data columns (total 24 columns):

id                  999999 non-null float64

click               999999 non-null int64

hour                999999 non-null int64

C1                  999999 non-null int64

banner_pos          999999 non-null int64

site_id             999999 non-null object

site_domain         999999 non-null object

site_category       999999 non-null object

app_id              999999 non-null object

app_domain          999999 non-null object

app_category        999999 non-null object

device_id           999999 non-null object

device_ip           999999 non-null object

device_model        999999 non-null object

device_type         999999 non-null int64

device_conn_type    999999 non-null int64

C14                 999999 non-null int64

C15                 999999 non-null int64

C16                 999999 non-null int64

C17                 999999 non-null int64

C18                 999999 non-null int64

C19                 999999 non-null int64

C20                 999999 non-null int64

C21                 999999 non-null int64

dtypes: float64(1), int64(14), object(9)

memory usage: 183.1+ MB

#我们随便来看下,app和device设备相关的字段长什么样子。

df[["id","click","app_id","app_domain","app_category","device_id","device_ip","device_model"]].head()

#从输出上,我们可以看到,其实虽然是object类型,还是一些设备编码什么的,后面我们会对这种数据类型进行转换。

#我们使用dataframe.describe拿到数据集的统计信息,.T来横向转置。

df.describe().T

#结果如下, 会统计各个字段的数量,针对于数字型的字段,mean均值,std标准差,min最小值,max最大值,以及各个分位段对应的值。

#100万的数据,不管是特征处理还是训练,我那一年5000大洋的阿里云服务器都跑得很辛苦,所以是非常不适合做练习用的,我们再截取部分。

##100万数据实在跑不过来,为了演示,再做截断

df_click_1 =  df[df["click"] == 1].iloc[:10000,:]

df_click_0 =  df[df["click"] == 0].iloc[:52400,:]

#然后合并回去(行合并),作为新的df

df=pd.concat([df_click_1, df_click_0])

df.info()

#数据结果

#依然按照5.24:1的比例来重新合并,使用df.concat做行合并,合并成只有11MB的数据了。

#对于数据长什么样子,有个基本的认识之后,我们来着手处理那些不能直接贯入到模型的字段,先构建一个简单函数。

##接下来对特征进行处理,先将类别特征进行编码

#针对类型类的特征,先进行编码,编码之前构建字典

from sklearn import preprocessing

def label_encode(field,df):

    dic = []

    df_field = df[field]

    list_field = df_field.tolist()

    #构建field字典,遍历全部,然后利用dict类型去重

    for i in list_field:

        if i not in dic:

            dic.append(i)

    #这里使用sklearn的特征处理方法,LabelEncode类别编码,即把所有类别根据字典映射为数字,比如1234这种,一般编码从1开始

    label_field = preprocessing.LabelEncoder()

    label_field.fit(dic)

    df_field_enconde_tmp = label_field.transform(df_field)

    df_field_enconde = pd.DataFrame(df_field_enconde_tmp, index=df.index, columns=[(field+'_enconde')])

    return df_field_enconde

#然后直接调用方法,针对site,app,device三类特征进行处理。

# site_id             999999 non-null object

# site_domain         999999 non-null object

# site_category       999999 non-null object

# app_id              999999 non-null object

# app_domain          999999 non-null object

# app_category        999999 non-null object

# device_id           999999 non-null object

# device_ip           999999 non-null object

# device_model        999999 non-null object

df_site_id_enconde = label_encode('site_id',df)

df_site_domain_enconde = label_encode('site_domain',df)

df_site_category_enconde = label_encode('site_category',df)

df_app_id_enconde = label_encode('app_id',df)

df_app_domain_enconde = label_encode('app_domain',df)

df_app_category_enconde = label_encode('app_category',df)

df_device_id_enconde = label_encode('device_id',df)

df_device_ip_enconde = label_encode('device_ip',df)

df_device_model_enconde = label_encode('device_model',df)

#这里的输出的就是已经把具体的label转化为编码的字段,接下来把这个处理好的,跟之前int类型的字段拼接回去,就形成了我们的输入。

df_input = pd.concat([df[['click','banner_pos','device_type','device_conn_type','C1','C14','C15','C16','C17','C18','C19','C20','C21']]

                      ,df_site_id_enconde

                      ,df_site_domain_enconde

                      ,df_site_category_enconde

                      ,df_app_id_enconde

                      ,df_app_domain_enconde

                      ,df_app_category_enconde

                      ,df_device_id_enconde

                      ,df_device_ip_enconde

                      ,df_device_model_enconde], axis=1) 

#这里先用df的指定字段的方式取到相关int特征,然后使用dataframe的concat 方法,链接多个df,注意axis=1的参数,这里意味着我们是从X轴方向进行拼接的,即有相同的行数,从列的维度进行拼接。

#那么,他们是如何保持映射的呢?在dataframe数据格式中,默认每个行都有一个index,每次变换都会保留index,所有这里concat会根据相同的index进行拼接合并。

#再来看下,处理过后的特征输入。

df_input.describe().T

#后面enconde尾缀的特征就是我们编码之后的特征,再加上一系列CXX神秘加密之后的特征(这个是数据贡献方,避免泄露一些关键信息,所以对部分公开的数据做特征名称的加密,让你不知道这个特征是具体什么意思,但确保是相关的信息)。

05

模型训练以及评估

#我们先把数据分为训练集和测试集,通常,我们会使用训练集来训练模型,然后使用测试集来验证模型的好坏。不断的实验,然后选择一个效果比较好的模型,所以切割数据是必然的过程。

#Sklearn机器学习库是一个非常强大的机器学习库,包括了各种你能想到的传统算法,以及机器学习的数据处理,特征处理,特征分析等等方法,非常强大。

#这里我们使用了sklearn中的model_selection模块,这个模块包含了大量特征处理和选择的方法,使用现成的train_test_split方法按指定比例分割。

from sklearn.model_selection import train_test_split

#对数据进行分割,分割为训练集和测试集

x_train,x_test,y_train,y_test = train_test_split(df_input.iloc[:,1:],df_input["click"],test_size=0.3, random_state=123)

#接下来数据有了,我们要开始训练了,先导入包。

##导入XGB相关的库

from xgboost import XGBClassifier

from sklearn import metrics

from sklearn.externals import joblib

#其中xgboost自然是主算法模型的包,metrics包含了很多各类型算法的评估方法,即评估你的算法好坏,然后joblib提供了模型的保存和加载的方法。

#训练好的模型保存之后,后续有新的数据,就可以直接load进来做预测或者分类,不需要额外再训练。

##进行xgboost拟合

import time 

begin_time = time.time()

print(f'Begin Time : {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(begin_time))}')

#受限于机器的资源,这里就不做gridsearch调参了,直接凑合着来(按最小资源消耗来设置参数)

model = XGBClassifier(learning_rate=0.1  #学习率

                     ,n_estimators=10   #训练的迭代轮次

                     ,max_depth=3      #树的深度

                     ,objective='binary:logistic'  #指定这是二分类模型

       )

model.fit(x_train, y_train, eval_metric="auc")

end_time = time.time()

print(f'End Time : {time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(end_time))}')

#这里我们只介绍已使用的模型参数,实际上参数有非常多,只能后面边学边来了,这里我们稍微的修改一下常见的作用较大的几个参数,后面我们再来深入理解。

#这点数据量,训练非常快,一下子就结束了,然后调用joblib把模型保存下来。

##保存xgb的model

joblib.dump(model, './model/xgb_model.pkl')

#然后简单的对模型进行评估

##效果输出函数

def func_print_score(x_data,y_data,data_type,model_x):

    y_pred = model_x.predict(x_data)

    print(f'==============({data_type})===================')

    confusion = metrics.confusion_matrix(y_data, y_pred)

    print(confusion)

    print('------------------------')

    auc = metrics.roc_auc_score(y_data,y_pred)

    print(f'AUC: {auc}')

    print('------------------------')

    accuracy = metrics.accuracy_score(y_data,y_pred)

    print(f'Accuracy: {accuracy}')

    print('------------------------')

    report = metrics.classification_report(y_data, y_pred)

    print(report) 

    print('=============================================')

#上面的函数,其实就是简单的把模型的AUC和Accuracy还有混淆矩阵(你可以认为实际划分的分布)打印出来了。

func_print_score(x_test,y_test,'testdata-xgb', model)

#结果如下

#第一个是混淆矩阵,由于之前我们分了30%的数据放到了test里头,所以,你可以认为这混淆矩阵的所有和就是所有test数据集合。

#15576代表本来click为0的预测成了0,196个0的预测成了1,然后2765个本来是1的预测成了0,183个本来是1的预测成了1。

#所以,从混淆矩阵的分布来看,预测1的情况非常糟糕。具体怎么衡量糟糕程度,我们通常会用AUC和accuracy还有precision和recall以f1。

#AUC暂时这个阶段可以放放,accuracy 你可以认为是(15576 +183)/所有四个值得和,可以认为是一种综合的准确率概念。但这个值受0的判断影响,看着很高,很好,实际上并不能代表真实的情况,因为我们需要预测1的出来。

#结合classification_report 结果来看,我们可以看到0的准确率和召回率(就是本来有多少,实际预测对了多少15576/(15576+196)计算出来的)是非常高的,但对于1的就非常低。

这表明这次的模型是非常失败的,因为预测的结果并不好,不好可能会在哪些环节呢?简直有太多地方可以优化了。

  • 数据量太少,本来数百万的样本,我们只去了6万多,意味着很多样本的特征根本没有学习到。

  • 模型完全没有调优,比如模型的参数是可以调整的,让模型表现更好。

  • 模型也可以选择,除了xgb,如果做分类,我们有太多的选择了。

  • 特征也可以进一步处理,比如低端点把一些没有用的特征干掉,比如不要一股脑灌进模型中去,高端点的可以组合特征,变成新的特征。

但是,不用着急是不,我们通过这个实例,把机器学习的基本过程理解透了就达到目标了。

06

实际生产差异

  • 这种基于python的jupyter过程的意义

我们通常在实际工作中,也常常使用jupyter来比如观测数据的形态,做一些模型的实验,因为通常如果数据量不大的话,pandas处理起来是非常快的。

所以能够快速的输出auc啊 或者accuracy的一些观测指标,或者用于做参数调整的实验等等。

  • 实际的数据从哪来

这里你看到的是已经处理好的csv,在实际的工作中,我们其实也是从各个源表或者数仓表,拉取用户的不同属性,最后join起来,形成id, click,特征列这种形态,最后export出来形成csv文件。

但是,很多时候我们的用户的特征(其实就是行为),每天是会变化,所以需要构建一个工作流,让用户的特征每天更新,然后每天更新模型,每天用新模型来预测数据。

工作流的构建简单点,可以用各种脚本通过crontab串起来,也可以用hadoop的一些工作流套件来构建,总之一个目的,最终要的是用户的属性列表。

  • 怎么部署模型

我们实验是用jupyter来摸索,但是实际部署中,如果是小数据量,你用工作流中把jupyter上的零散语句,写成一个python文件就好了。

然后拿到model文件,然后同样用一个predict的python文件来处理离线的数据就好了。

如果是在线的或者近实时的,把model加载到python代码服务中,或者java服务中,很多库都提供了跨语言的加载预测逻辑。

特别是python本来跟java都是属于jvm系的,很多都天然支持。

如果是数据量巨大的,可能我们就不会使用单机版的了,比如会用spark版本之类的库。

07

总结

一个教程装不下太多的东西,这个教程唯一的目的就是让你了解这个基本过程,接触一下这是怎么回事,怎么玩的。

然后如果你愿意,为此搭建基础的运行环境(python、jupyter,xgb,sklearn),然后把这个实例跑起来。

如果还想继续这条路下去,这只是个开端,还有很多需要解决的。比如样本怎么筛选,特征如何筛选,模型如何怎么筛选,选定模型之后参数怎么调,调好参数之后模型具体怎么评估,怎么选择模型,然后怎么根据应用场景来选择算法等等。

所以,重要的是行动起来,先把这个阶段的东西学起来,然后缺什么补什么,逐步补充知识结构,从一个CASE开始,到环境整理好,数据整理好,然后python的语法,一些常见的机器学习库的使用,怎么调优评估。

知识星球

欢迎加入一个学习数据/机器学习相关知识圈子,这里有相关起步的朋友,对于算法感兴趣的朋友,对数据感兴趣的朋友,有浓厚的交流氛围,以及每周周末都有相关的学习实例,以及针对实例的沟通交流和辅导。

这个星球并不适合所有朋友,加入星球可以获取相关的星球沉淀知识,还可以加入专门的沟通学习交流群,一起成长,这些人可能适合。

【01】刚毕业计算机相关专业的小朋友,想进入数据行业。

【02】从事数据开发/分析岗位的朋友,想学习一些机器学习或者往数据挖掘方向发展。

【03】对算法或者机器学习感兴趣的朋友。

【04】对推荐系统或者广告算法应用相关感兴趣的朋友,我们在星球中,同样会有大量广告与数据相关的内容。

文章都看完了,还不点个赞来个赏~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值