2015年,全球互联网广告营收接近600亿美元,比2014年增长了近20%。多家互联网巨头都依赖于广告营收,如谷歌,百度,Facebook,互联网新贵们也都开始试水广告业,如Snapchat, Pinterest, Spotify.
作为互联网广告的老大哥,谷歌花了很大的力气研发自己的社交网络,Google+,并期待能与Facebook,Twitter抗衡。然后事与愿违,Google+的份额依然低于1% 。
2015年,谷歌终于不再强迫用户把Google+和谷歌家的其他服务绑定,如Youtube。笔者认为谷歌花大量人力财力研发Google+并将其与其他服务绑定的原因之一是搜集用户的喜好数据来为自家的广告业务服务。更具体的说是为了更好地预测广告的点击率。在这方面,Facebook似乎更胜一筹。2015年第一季度,Facebook广告的转换率比Google Display Ads Network (GDN)高出了55%。笔者并不感到惊讶,Facebook显然比谷歌更了解用户的喜好,笔者猜测用户的行为,包括点赞, 评论,关注等,都为广告算法提供了关于用户的喜好或类别的信息。这些信息会用于广告的点击率预测,而点击率预测又会应用于广告排序算法。
今天我就带大家来用 Spark MLlib训练一个广告点击率预测的模型。
环境配置
Java
Spark 2.0.0
安装很简单,pre-built版本的spark下载下来即可
Downloads | Apache Spark
Python
数据
Kaggle Avazu挑战赛中的广告数据
Click-Through Rate Prediction
元数据
id: ad identifier
click: 0/1 for non-click/click
hour: format is YYMMDDHH, so 14091123 means 23:00 on Sept. 11, 2014 UTC.
C1 -- anonymized categorical variable
banner_pos
site_id
site_domain
site_category
app_id
app_domain
app_category
device_id
device_ip
device_model
device_type
device_conn_type
C14-C21 -- anonymized categorical variables
数据预处理
我们注意到数据中有字符串类型的值,如site_id,site_domain,site_category,app_id,app_domain,app_category等。我们需要将他们转换成数值型,公式如下:
代码:
import os
import sys
if __name__ == "__main__":
input_file = sys.argv[1]
preprocess_file = sys.argv[2]
test_flag = sys.argv[3]
print "input=" + input_file
print "preprocess_file=" + preprocess_file
output = open(preprocess_file, "w")
with open(input_file, "r") as lines:
next(lines)
for line in lines:
fields = line.split(",")
index = 5
end = 14
if test_flag == "1":
index = 4
end = 13
while index < end:
fields[index] = str(hash(fields[index]) % 1000000)
index += 1
newline = ",".join(fields)
output.write(newline)
output.close()
此外,有必要将原来的训练数据分割成两部分:本地训练数据,本地测试数据。由于原来的训练数据是基于10天的数据,我们可以把前9天数据作为本地训练数据,把最后一天的数据作为本地测试数据。
import os
import sys
if __name__ == "__main__":
input_file = sys.argv[1]
train_file = sys.argv[2]
test_file = sys.argv[3]
test_start_date = sys.argv[4]
test_data_ouput = open(test_file, "w")
train_data_output = open(train_file, "w")
with open(input_file, "r") as lines:
next(lines)
for line in lines:
fields = line.split(",")
if fields[2].startswith(test_start_date):
test_data_ouput.write(line)
else:
train_data_output.write(line)
train_data_output.close()
test_data_ouput.close()
sconf = SparkConf().setAppName(“WordCount") .setMaster(“local[4]")
sc = SparkContext(conf=sconf)
这段代码会创建一个4线程的SparkContext对象,并将其相应的任务命名为WordCount。
rddFromTextFile = sc.textFile(“sample.txt")
上述代码中的textFile函数(方法)会返回一个RDD对象。该对象的每一条记录都是一个表示文本文件中某一行文字的String(字符串)对象。
from __future__ import print_function
from pyspark import SparkContext
from pyspark.mllib.classification import LogisticRegressionWithLBFGS,
LogisticRegressionModel
from pyspark.mllib.regression import LabeledPoint
from pyspark.mllib.util import MLUtils
if __name__ == "__main__":
sc = SparkContext(appName="CTRLogisticRegression")
# $example on$
# Load and parse the data
def parsePoint(line):
values = [float(x) for x in line.split(',')]
features = values[0:1]
features.extend(values[2:])
return LabeledPoint(values[1],features)
data = sc.textFile("train_data")
testData = sc.textFile("test_data")
parsedTrainData = data.map(parsePoint)
parsedTestData = testData.map(parsePoint)
# Build the model
model = LogisticRegressionWithLBFGS.train(parsedTrainData)
# Evaluating the model on training data
labelsAndPreds = parsedTestData.map(lambda p: (p.label,
model.predict(p.features)))
trainErr = labelsAndPreds.filter(lambda (v, p): v != p).count() /
float(parsedTestData.count())
print("Training Error = " + str(trainErr))
# Save and load model
model.save(sc, "target/tmp/CTR")
代码执行命令