2020腾讯广告算法大赛——算法小白的复盘

2022-05-09 19:24:44 浏览数 (1)

阅读助手

  • 写在前面
  • 赛题介绍
  • 个人赛况
  • 代码开源-score 1.2
    • 【00】数据导入TI-ONE
    • 【01】按userid聚合(groupby)特征
    • 【02】word2vec训练
    • 【03】数据特征化
    • 【04】lgb模型训练
    • 【05】test分批次预测
    • 【06】合并和提交到COS存储桶
  • 参考资料

写在前面

全文共计11958字,请合理使用目录(阅读助手)辅助阅读

《2020腾讯广告算法大赛》复赛已经接近尾声,作为一瓶初赛酱油,打算做个复盘,留个笔记,本来初赛结束就打算写的,被各种事情耽搁了,直到今天才动手开写

先说下个人情况:某电商公司任职,数据分析方向,做的大部分是DBA和爬虫的活(从过往博客也看的出来),了解过sklearn库的一些传统机器学习(eg:决策树、随机森林、Kmeans、逻辑回归等基础传统机器学习算法),(卷积)神经网络,lgb,lstm,W2V,RNN等都是在比赛过程中听大佬们分享后现学的(虽然还是不太会用,原理也推不出来,至少比一开始听见这些词都一脸懵逼要好太多太多 了),

可以说是算法纯小白,最终成绩:score — 1.269806;排名 — 367

赛题介绍

题目:广告受众基础属性预估

赛题描述 本届算法大赛的题目来源于一个重要且有趣的问题。众所周知,像用户年龄和性别这样的人口统计学特征是各类推荐系统的重要输入特征,其中自然也包括了广告平台。这背后的假设是,用户对广告的偏好会随着其年龄和性别的不同而有所区别。许多行业的实践者已经多次验证了这一假设。然而,大多数验证所采用的方式都是以人口统计学属性作为输入来产生推荐结果,然后离线或者在线地对比用与不用这些输入的情况下的推荐性能。本届大赛的题目尝试从另一个方向来验证这个假设,即以用户在广告系统中的交互行为作为输入来预测用户的人口统计学属性。

我们认为这一赛题的“逆向思考”本身具有其研究价值和趣味性,此外也有实用价值和挑战 性。例如,对于缺乏用户信息的实践者来说,基于其自有系统的数据来推断用户属性,可以帮助其在更广的人群上实现智能定向或者受众保护。与此同时,参赛者需要综合运用机器学习领域的各种技术来实现更准确的预估。

具体而言,在比赛期间,我们将为参赛者提供一组用户在长度为91天(3 个月)的时间窗 口内的广告点击历史记录作为训练数据集。每条记录中包含了日期(从1到91)、用户信息(年龄,性别),被点击的广告的信息(素材 id、广告 id、产品 id、产品类目 id、广告主id、广告主行业id等),以及该用户当天点击该广告的次数。测试数据集将会是另一组用户的广告点击历史记录。提供给参赛者的测试数据集中不会包含这些用户的年龄和性别信息。

本赛题要求参赛者预测测试数据集中出现的用户的年龄和性别,并以约定的格式提交预测结果。大赛官网后台将会自动计算得分并排名。详情参见【评估方式】和【提交方式】。 初赛和复赛除了所提供的训练数据集的量级有所不同之外,其他设置均相同。

提交方式 参赛者提交的结果为一个带标题行的 submission.csv 文件,编码采用无 BOM 的 UTF-8, 具体格式如下(字段顺序以下面的描述为准,各字段用逗号分隔,中间无空格): ⚫ user_id ⚫ predicted_age: 预测的用户年龄分段,取值范围[1,10]。 ⚫ predicted_gender: 预测的用户性别,取值范围[1,2]。 测试数据集中每个用户均应在submission.csv文件中对应有且仅有一行预测结果。各用户 的预测结果在该文件中的出现顺序与评估结果无关。

评估方式 大赛会根据参赛者提交的结果计算预测的准确率(accuracy)。年龄预测和性别预测将分别评估准确率,两者之和将被用作参赛者的打分。

测试数据集会和训练数据集一起提供给参赛者。大赛会将测试数据集中出现的用户划分为两组,具体的划分方式对参赛者不可见。其中一组用户将被用于初赛和复赛阶段除最后一天之外的排行榜打分计算,另一组则用于初赛和复赛阶段最后一天的排行榜打分计算,以及最后的胜出队伍选择。

2020腾讯算法大赛初赛数据集下载【提取码:46lw】

个人赛况

阶段一:score 0.89 ①3 1 descirbe() 对train训练数据描述可得 年龄age3的概率最大,性别gender1的概率最大 直接提交 预测结果age3 gender1 score就可达到0.893

②word2vec训练参数错误 在参考了各路大佬的w2v方案后依然没有突破 0.89,一度弃赛,在某个月黑风高的夜晚,突然来了一手神来之笔,查看W2V训练后的词向量,结果发现训练出来的词向量是0-9,

阶段二:score 1.2 word2vec Lgb 纠正了word2vec训练的词向量之后就达到了1.2 由于深度学习啥也不懂,还是没有达到大佬们的baseline

解题思路(代码思路)——copy的各路大佬的基本思路: 把每个点击的广告id当作一个词,把一个人90天内点击的广告id列表当作一个句子,使用word2vec来制作一个“word embedding”,把问题转化成一个NLP题目;把广告id的embedding喂入lgb——一度试图把所有的的特征组合训练wordembedding,keras内存爆炸 ,后来试了挑选三个进行组合,score反而下降了,可能组合姿势有问题

代码开源-score 1.2

【00】数据导入TI-ONE

代码语言:javascript复制
#安装&导入库
!pip install wget
import wget,tarfile,zipfile

#压缩数据包下载
filename = wget.download("https://tesla-ap-shanghai-1256322946.cos.ap-shanghai.myzijiebao.com/cephfs/tesla_common/deeplearning/dataset/algo_contest/train_preliminary.zip")
print(filename)

#解压缩
zFile = zipfile.ZipFile(filename, "r")
for fileM in zFile.namelist():
    zFile.extract(fileM, "./")
    print(fileM)
zFile.close();

【01】按userid聚合(groupby)特征

代码语言:javascript复制
#导入库
import tensorflow as tf
import pandas as pd
import numpy as np
import gc

#聚合去重并计数
def get_groupby_data(data,group_zd='user_id',pj_zd='ad_id'):
    data_dict=dict(list(data.groupby(group_zd)))
    result=[]
    for key in data_dict.keys():
        result.append([key,str(data_dict[key][pj_zd].to_list())[1:-1].replace(',','')])
    pd_result=pd.DataFrame(result)
    pd_result.columns=[group_zd,pj_zd]
    return pd_result
    
#设置聚合特征
# zd_list=['ad_id','product_category','advertiser_id', 'time', 'click_times']
zd_list=['creative_id']

#train_data读取
train_ad=pd.read_csv("../train_preliminary/ad.csv",na_values="\N")
train_click_log=pd.read_csv("../train_preliminary/click_log.csv",na_values="\N")
train_user=pd.read_csv("../train_preliminary/user.csv",na_values="\N")
data=pd.merge(pd.merge(train_ad,train_click_log),train_user)

#内存清理
del train_ad,train_click_log
gc.collect()

#train_data聚合
for zd in zd_list:
    new_data=data[[zd,'user_id']]
    new_data=get_groupby_data(new_data,pj_zd=zd)
    new_data=pd.merge(new_data,train_user)
    new_data.to_csv(zd "/groupby_data/train_%s.csv"%zd,index=False)
    print("-"*10 zd '汇总完成' '-'*10)


#test_data读取
test_ad=pd.read_csv("../test/ad.csv")
test_click_log=pd.read_csv("../test/click_log.csv")
test_data=pd.merge(test_ad,test_click_log)

#test_data聚合
for zd in zd_list:
    test_data_new=test_data[[zd,'user_id']]
    test_data_new=get_groupby_data(test_data_new,pj_zd=zd)    
    test_data_new.to_csv(zd "/groupby_data/test_%s.csv"%zd,index=False)
    print("-"*10 zd '汇总完成' '-'*10)

【02】word2vec训练

代码语言:javascript复制
#导入库
import tensorflow as tf
import pandas as pd
import numpy as np
import os
!pip install gensim
import gc
from gensim.models.word2vec import Word2Vec

# 设定词向量训练的参数
num_features = 128   # Word vector dimensionality
min_word_count = 0   # Minimum word count
num_workers = 4       # Number of threads to run in parallel
context = 10         # Context window size
model_name = '{}features_{}minwords_{}context.model'.format(num_features, min_word_count, context)

#设置训练特征
# zd_list=['creative_id','ad_id','product_category','advertiser_id', 'time', 'click_times']
zd_list=['creative_id']

#词向量训练
for zd in zd_list:
    new_data=pd.read_csv(zd "/groupby_data/train_%s.csv"%zd,na_values="\N")
    new_data2=pd.read_csv(zd "/groupby_data/test_%s.csv"%zd,na_values="\N")
    new_data3=pd.concat([new_data,new_data2])
    del new_data,new_data2
    gc.collect()#清理内存
    sentences_list = []
    #注意:需要split传入list,第一次没有split训练的词模型是0-9
    for line in new_data3[zd]:
        sentences_list.append(line.split())
    model = Word2Vec(sentences_list, workers=num_workers, 
                size=num_features, min_count = min_word_count, 
                window = context)
    model.save(os.path.join(zd, 'models', model_name))
    print("-"*10 zd '训练完成' '-'*10)

【03】数据特征化

代码语言:javascript复制
#导入库
import tensorflow as tf
import pandas as pd
import numpy as np
import os
!pip install gensim
from gensim.models.word2vec import Word2Vec

def to_review_vector(review):
    global word_vec
    word_vec = np.zeros((1,300))
    for word in review.split():
        #word_vec = np.zeros((1,300))
        if word in fastmodel:
            word_vec  = np.array([fastmodel[word]])
    #print (word_vec.mean(axis = 0))
    return pd.Series(word_vec.mean(axis = 0))

#由于机器限制,数据分段特征化&存储
#zd_list=['creative_id','ad_id','product_category','advertiser_id', 'time', 'click_times']
model_name='300features_0minwords_10context.model'
# zd_list=['product_category','advertiser_id', 'time', 'click_times']
zd_list=['creative_id']
for zd in zd_list:
    data=pd.read_csv(zd "/groupby_data/train_%s.csv"%zd)
    try:
        os.mkdir(zd "/train_feature")
    except:
        pass
    fastmodel=Word2Vec.load(zd '/models/' model_name)
    count=0
    mm=0
    for i in range(100000,len(data) 1,100000):
        train_data_features = data[count:i][zd].apply(to_review_vector)
        w2c_data=pd.concat([data[count:i][['user_id','age','gender']],train_data_features],axis=1)
        w2c_data.to_csv(zd "/train_feature/w2c_data_%s%s.csv"%(zd,mm),index=False)
        print("---%s第 %s 段数据特征向量化完成---"%(zd,mm))
        mm =1
        count=i

【04】lgb模型训练

代码语言:javascript复制
#导入库
!pip install lightgbm
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import tensorflow as tf
import pandas as pd
import numpy as np
import os
import gc

# zd_list=['creative_id','ad_id','product_category','advertiser_id','time','click_times']
zd_list=['creative_id']

#分段特征数据合并
for zd in zd_list:
    path=zd "/train_feature/"
    wjs=os.listdir(path)
    data=pd.DataFrame()
    for wj in wjs:
        data_path=path wj
        data0=pd.read_csv(data_path)#.drop(zd,axis=1)
        data=data.append(data0)
        print(len(data))
    data.columns=[zd '_' cc if cc not in ['user_id', 'age', 'gender'] else cc for cc in data.columns]
    if zd==zd_list[0]:
        data1=data
    else:
        data1=pd.merge(data1,data)
    gc.collect()#清理内存
    print('!'*10 zd '拼接完成')
    
#内存清理
del data,data0
gc.collect()

#age模型训练
train_x=data1[data1.columns.difference(['user_id','age','gender'])]
target=data1.age.astype(int)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(train_x, target, test_size=0.2)
del train_x,data1
gc.collect()#清理内存
print("Train data length:", len(X_train))
print("Test data length:", len(X_test))
# 转换为Dataset数据格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)

# 参数
params = {
    'task': 'train',
    'boosting_type': 'gbdt',  # 设置提升类型
    'objective': 'multiclass',  # 目标函数
    'num_class': 11,  
#     'metric': {'l2', 'auc'},  # 评估函数
    'num_leaves': 31,  # 叶子节点数
    'learning_rate': 0.05,  # 学习速率
    'feature_fraction': 0.9,  # 建树的特征选择比例
    'bagging_fraction': 0.8,  # 建树的样本采样比例
    'bagging_freq': 5,  # k 意味着每 k 次迭代执行bagging
    'verbose': 1  # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
}

# 模型训练
gbm = lgb.train(params, lgb_train, num_boost_round=1000, valid_sets=lgb_eval, early_stopping_rounds=5)
# 模型保存
gbm.save_model('model_age.txt'
# 模型加载
gbm = lgb.Booster(model_file='model_age.txt')
# 模型预测
y_prob = gbm.predict(X_test, num_iteration=gbm.best_iteration)

arr=pd.DataFrame(y_prob)
arr['max_value']=arr.max(axis=1)
arr['max_index']=np.argmax(y_prob,axis=1)
y_pred1=arr['max_index']

# 模型评估
print('The rmse of prediction is:', mean_squared_error(y_test,y_pred1) ** 0.5)


#gender模型训练
train_x=data1[data1.columns.difference(['user_id','age','gender'])]
target=data1.gender.astype(int)
# 划分训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(train_x, target, test_size=0.2)
del train_x,data1
gc.collect()#清理内存
print("Train data length:", len(X_train))
print("Test data length:", len(X_test))
# 转换为Dataset数据格式
lgb_train = lgb.Dataset(X_train, y_train)
lgb_eval = lgb.Dataset(X_test, y_test, reference=lgb_train)

# 参数
params = {
    'task': 'train',
    'boosting_type': 'gbdt',  # 设置提升类型
    'objective': 'multiclass',  # 目标函数
    'num_class': 11,  
#     'metric': {'l2', 'auc'},  # 评估函数
    'num_leaves': 31,  # 叶子节点数
    'learning_rate': 0.05,  # 学习速率
    'feature_fraction': 0.9,  # 建树的特征选择比例
    'bagging_fraction': 0.8,  # 建树的样本采样比例
    'bagging_freq': 5,  # k 意味着每 k 次迭代执行bagging
    'verbose': 1  # <0 显示致命的, =0 显示错误 (警告), >0 显示信息
}

# 模型训练
gbm = lgb.train(params, lgb_train, num_boost_round=1000, valid_sets=lgb_eval, early_stopping_rounds=5)
# 模型保存
gbm.save_model('model_gender.txt')
# 模型加载
gbm = lgb.Booster(model_file='model_gender.txt')
# 模型预测
y_prob = gbm.predict(X_test, num_iteration=gbm.best_iteration)

arr=pd.DataFrame(y_prob)
arr['max_value']=arr.max(axis=1)
arr['max_index']=np.argmax(y_prob,axis=1)
y_pred1=arr['max_index']

# 模型评估
print('The rmse of prediction is:', mean_squared_error(y_test,y_pred1) ** 0.5)

【05】test分批次预测

代码语言:javascript复制
#导入库
!pip install lightgbm
import lightgbm as lgb
from sklearn.metrics import mean_squared_error
from sklearn.datasets import load_iris
from sklearn.model_selection import train_test_split
import tensorflow as tf
import pandas as pd
import numpy as np
import gc
import os

# zd_list=['creative_id','ad_id','product_category','advertiser_id', 'time', 'click_times']
zd_list=['creative_id']
for zd in zd_list:
    path=zd "/test_feature/"
    wjs=os.listdir(path)
#     wjs=os.listdir(path)[5:]
    data=pd.DataFrame()
    for wj in wjs:
        data_path=path wj
        data0=pd.read_csv(data_path).drop(zd,axis=1)
        data=data.append(data0)
        del data0
        gc.collect()#清理内存
        print(len(data))
    data.columns=[zd '_' cc if cc not in ['user_id'] else cc for cc in data.columns]
#     data.columns=[zd '_' cc if cc not in ['user_id', 'age', 'gender'] else cc for cc in data.columns]
    if zd==zd_list[0]:
        data1=data
    else:
        data1=pd.merge(data1,data)
    del data
    gc.collect()#清理内存
    print('!'*10 zd '拼接完成')

# data1=data1[:500000]
data1=data1[500000:]

result=data1[['user_id']].reset_index(drop=True)
test_X=data1[data1.columns.difference(['user_id'])]
del data1
gc.collect()#清理内存
# 模型加载
gbm = lgb.Booster(model_file='model_age.txt')
# 模型预测
y_test = gbm.predict(test_X, num_iteration=gbm.best_iteration)
arr=pd.DataFrame(y_test)
arr['max_value']=arr.max(axis=1)
arr['max_index']=np.argmax(y_test,axis=1)
result['age']=arr['max_index']


# 模型加载
gbm = lgb.Booster(model_file='model_gender.txt')
# 模型预测
y_test = gbm.predict(test_X, num_iteration=gbm.best_iteration)
arr=pd.DataFrame(y_test)
arr['max_value']=arr.max(axis=1)
arr['max_index']=np.argmax(y_test,axis=1)
result['gender']=arr['max_index']


result.columns=['user_id','predicted_age','predicted_gender']
# result.to_csv('submission1.csv',index=False)
result.to_csv('submission2.csv',index=False)
print(len(result))

【06】合并和提交到COS存储桶

代码语言:javascript复制
import pandas as pd
import numpy as np

#文件合并
data1=pd.read_csv("submission1.csv")
data2=pd.read_csv("submission2.csv")
data=pd.concat([data1,data2])
print(len(data))
data.to_csv("submission.csv",index=False)

from ti import session
ti_session = session.Session()
# path:结果文件的路径
# bucket:指定存储桶。
# key_prefix:存储桶下的 cos 路径
inputs = ti_session.upload_data(path="submission.csv", bucket="game-gt-1252065438", key_prefix="test")

参考资料

【00】如何使用 TI-ONE Notebook 玩转算法大赛

【01】易观性别年龄预测chizhu大佬的冠军开源

【02】2020腾讯广告算法大赛:赛题理解与解题思路

【03】2020腾讯广告算法大赛基本思路(线上1.3 )

【04】2020腾讯广告算法大赛:如何突破分数瓶颈?

【05】2020腾讯广告算法大赛:高分进阶

【06】大神干货:冠军选手分享解题思路,助你轻松突围初赛

【07】高分选手讲解:如何突破思维圈限,从NLP角度挖掘新的解题思路

【08】超值赛题分享大礼包,你的“六一”礼物来咯!

【09】【王牌选手分享】一发问鼎!鹅厂大神上分思路,助你玩转初赛!

【10】【前排选手分享】初赛尾声将至,大神带你最后一搏!

【11】【特色团队采访】1 1 1>3?看新人团队如何高效合作

特别感谢各位大佬的分享,虽然最终没进复赛,也算感受了一波算法的魅力,尤其感谢鱼佬的耐心解惑;最后还得特别感谢下腾讯智能钛机器学习 TI-ML,要不然我那渣渣机器连 数据都读不完就内存溢出了,确实挺好用的,我只用了notebook,已经集成好了tensflow环境,贼方便,还有其他机器学习操作,没玩转,对notebook比较熟!全程都是notebook,而且对于社畜而言还有个好处就是云上同步,不论在哪我都不用把代码倒腾过来倒腾过去,只要连网就可以看到你原先操作的代码,甚至于在公司点了运行,坐个地铁到家直接登陆看结果!从一开始感觉特别麻烦不习惯,到最后的”真香"!!!没人逃得掉王镜泽定律(没收鹅厂广告费 ,麻烦腾讯云看到了给打笔广告费)

0 人点赞