阿里推荐大赛:ODPS SQL 构建离线评估

上一篇文章介绍了 ODPS SQL 的大概使用方法,几个 tips,和讲到一半的离线评估。现在上来把上次的坑填完。希望对于还没有开始离线调优的团队有点帮助。

划分训练集、验证集

回顾训练集、验证集的划分。

根据时间,可以将前三月划分为训练集:

create table train_set as
select * 
from t_alibaba_bigdata_user_brand_total_1
where visit_datetime <= '07-15';

同理,验证集如下,注意去重:

create table validate_set as
select
    user_id,
    wm_concat(',',brand_id) as brand
from(
    select distinct user_id,brand_id
    from t_alibaba_bigdata_user_brand_total_1
    where type = '1' and visit_datetime > '07-15'
)a
group by user_id;

这里distinct关键字的作用就是去重,当然也可以用group by实现。两者效果一样,那效率呢?在 MySQL 中(如果没记错的话,天池的底层数据库是基于 MySQL 的),两者的实现原理很相似。区别在于distinct会读取所有的记录,而group by是在分组之后每组只返回一条记录,也就是说后者读取的条数要少很多,效率会更高一些。所以可以用以下代码代替上述的子查询:

select user_id,brand_id
from t_alibaba_bigdata_user_brand_total_1
where type = '1' and visit_datetime > '07-15'
group by user_id,brand_id

扯远了… 回来吧。计算推荐集、验证集条数的方法之前也给出了:

select sum(regexp_count(brand,',')+1) from t_tmall_add_user_brand_predict_dh;

接下来要计算推荐集和验证集之间的重复条数,这里可以用 UDF 方便地实现。

UDF 计算重复条数

在我们 Eclipse 中的 ODPS 项目下新建 UDF。填入 Package 名称为:jark.udf,和 UDF 类名:CountHits,点击确认。插件会自动帮我们生成jark.udf包用于写 UDF 代码,和test.jark.udf包用于本地测试。

编辑CountHits.java文件,注意下文的Long不能替换为long类型,否则在ODPS上运行会报Method Not Found错误。:

public class CountHits extends UDF {
    public Long evaluate (String a,String b){
        if(a == null || b == null) return 0L;     //异常值
        Set<String> set1 = new HashSet<String>();
        Set<String> set2 = new HashSet<String>();
        set1.addAll(Arrays.asList(a.split(",")));
        set2.addAll(Arrays.asList(b.split(",")));
        Long hits = 0L;
        for(String s : set2){
            if( set1.contains(s) )
                hits++;
        }
        return hits;
    }
}

本段函数的主要工作是在a串和b串去重后,计算它们中重复元素的个数。接下来在本地测试下 UDF 。在test.jark.udf包中有两个文件TestCountHits.javaTestUDFBase.java。由于如果 UDF 的输入参数是多个的话,本地测试默认的分隔符是逗号,与我们brand中的逗号冲突了。所以修改下TestUDFBase.java文件的第15行的分隔符为空格(当然也可以其他符号):

private final static String ODPS_SEPARATOR = " ";

顺便修改第72行,去掉(String),原因是Long型不能强制转换成String

return callMeth.invoke(UDFClass, input_parameter.toArray()) + "\n";

TestCountHits.in文件中输入测试输入:

123456,444,555,666 123456,666,777,888
888,999 111,222
111,111,222,222 111,222,222,333

运行TestCountHits.java后,在TestCountHits.out文件中得到测试结果:

2
0
2

测试输入应更为复杂,以上只是示例。在确认 UDF 没有问题后,准备上传。将Package jark.udf打成 jar 包,命名为jark-udf.jar,置于C:/TOOLS下,执行以下命令:

create resource jar C:/TOOLS/jark-udf.jar
create function count_hits jark.udf.CountHits jark-udf.jar

上述命令作用分别是将用户 jar 包上传到 ODPS 和在 ODPS 上注册函数并命名为count_hits。现在使用ls functions命令应该就能看到我们刚刚注册的函数了。

现在,我们就能像调用一般内建函数一样调用我们自己的count_hits函数了。计算推荐集和验证集中的重复条数,有下面代码:

select sum(count_hits(a.brand,b.brand)) hits from t_tmall_add_user_brand_predict_dh a 
join validate_set b on a.user_id = b.user_id;

上面演示了一般 UDF 的创建使用过程,其他类似的 UDF 都可以参考以上过程构建。UDF 是 SQL 的一大工具 ,很多规则算法都可以用过 UDF 方便地实现。

计算评测值

我们知道准确率的计算公式:$precision = hits / pnums$,为pnums为推荐条数。

召回率的计算公式有:$recall = hits / vnums$,vnums为验证集条数。

而 F1 的计算公式有:

为了计算方便,我们用full outer join连接验证集和推荐集,并将计算出的hitspnumsvnums 放到临时表里。感谢 @木同学style 的建议。

select sum(count_hits(p.brand,v.brand) hits,
       sum(regexp_count(p.brand,',')+1) pnums,
       sum(regexp_count(v.brand,',')+1) vnums
from t_tmall_add_user_brand_predict_dh p
     full outer join validate_set v on p.user_id = v.user_id

有了这三个值后,就可以用上面的公式轻松计算出评测值了。

select (hits/pnums) precision, (hits/vnums) recall,(2*hits/(pnums+vnums)) F1
from (
    select hits , pnums , vnums
    from ...
)a;

OK,感觉讲的有点啰嗦了… 整合上面计算评测值的代码后,有如下代码:

create table evaluation as
select (hits/pnums) precision , (hits/vnums) recall , (2*hits/(pnums+vnums)) F1 , 
        hits , pnums , vnums , getdate() eval_time
from (
    select sum(count_hits(p.brand,v.brand) hits,
           sum(regexp_count(p.brand,',')+1) pnums,
           sum(regexp_count(v.brand,',')+1) vnums
    from t_tmall_add_user_brand_predict_dh p
         full outer join validate_set v on p.user_id = v.user_id
)a;

我们将评测值写到了evaluation表中,可供组员查看分析。运行一次上面代码后,若以后再进行评测,可以将第一行改成insert into/overwrite table evaluation后直接运行,个人喜欢用into,这样可以与上次的结果进行比较。

现在已有的模型算法就可以在本地测试调优后,再上传到线上进行评估了。

关于数据集的几点想法

对于离线评估我也并没有深刻的见解,只是想无责任地说点个人的想法,还望大家一起讨论纠正。

  1. 上面的训练集和验证集的划分有一定的局限性。主要是模型可能会高度依赖于训练集和验证集的构成,导致过拟合的问题。要消除这种依赖,可以借鉴自助法,每次训练时从训练集中随机抽取一部分,验证时也随机从验证集中抽取一部分,抽取比例一样,例如抽取63.2%。XLab/XLib 提供了随机采样的接口。

  2. 线上预测我们是用前四个月预测第五个月,时间划分是 4:1 的关系。所以离线划分训练集、验证集时,也按这个比例划分会更合理。即最后24天的数据作为验证集。

  3. 上面的离线评估是用来训练模型用的,当我们后期融合模型时如果继续在训练集上融合,还是会造成过拟合。所以可以在训练集上再拆分成训练集 A 和预测集 B,B 的生成方法和验证集类似,也取最后 1/5。在 A 上训练模型,B 上做出预测,得出融合系数。再在整个训练集上训练模型,在验证集上做出预测。最后将预测结果用得到的融合系数融合,融合结果再在验证集上得到最终预测结果。关于模型融合,是一门高深的学问,希望有经验的大大出来分享一下。

  4. 原始数据集噪声很大,相信很多同学都发现有垃圾用户、刷钻用户的存在。《推荐系统实践》里说「使用正确的用户数据对推荐系统至关重要」。所以,在忙着实现各种模型算法之前不如先做下数据清理。去噪的方法有很多,比如基于规则、基于离群点检测、基于聚类等等。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 适合毕业设计、课程设计作业。这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。 所有源码均经过严格测试,可以直接运行,可以放心下载使用。有任何使用问题欢迎随时与博主沟通,第一时间进行解答!

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值