使用AdWords平衡算法给广告分配关键字

广告已经成为Web收入主要模式。这是一项数十亿美元的业务,也谷歌大部分收入来源。此外,在互联网领域存在这样的可能性,像谷歌这样的公司以免费的形式提供它的主要服务,而通过广告赚取公司的利润。

让我们来探讨如何使用MapReduce实现一个简单的Adwords平衡算法。

Adwords允许客户竞投关键字。例如,广告主“A”可以用2美元价格竞购关键字“Hadoop支持”,提供100美元的最大预算,广告主的“B”会竞购关键词“Hadoop支持”为1.50美元,提供最高预算200美元。当用户为一个文件中搜索与给定的关键字时,系统将选择的出价这些关键字中的一个或多个广告。只有当用户点击广告时,广告客户才支付费用。

Adwords问题是如何显示广告,使得其将收益最大化。在设计此类解决方案时,有几个需要考虑的场景因素:

  • 只有用户点击,而不是显示广告,才能够给我们带来收入。因此,我们要显示可能被经常点击的广告。这里使用一个广告点击次数与广告显示次数的比值,我们称此为关键字的“点击通过率”。
  • 我们想展示那些拥有大笔预算的人们,因为这些都可能是那些有硬花钱的用户,而不是更小预算的用户。

在本章攻略中,我们将实现可以在这种情况下使用的AdWords帐户的余额算法的简化版本。为简单起见,我们假设广告主出价仅在单个关键字。此外,由于我们无法找到一个真正的出价数据集,我们将生成一个模拟出价数据集。

假设你去支持基于关键字的广告系统,使用亚马逊的数据集。攻略摘要如下:

  • 第一道MapReduce的任务将使用亚马逊销售指数估算出关键字的点击通过率
    。这里,我们假定存在于标题的关键字,
    如果该产品具有较高的销售排名将有更好的点击率。
  • 然后,我们将运行一个Java任务生成一个出价的数据集。
  • 然后第二道MapReduce的任务将出价相同的产品分组在一起,创建一个输出是否适合于所使用的广告分配方案。
  • 最后,我们将使用一个广告分配方案,以关键字分配给广告客户。我们将使用的Adword平衡算法,它使用下面的公式,根据各广告主拥有的未动用的预算,投标价值,并点击率的分数下列公式分配优先级。
Measure = bid value * click-through rate * (1-e^(-1*current budget/ initial budget))

准备工作

下面的步骤描述了如何准备运行Adwords示例:

  1. 这里假设你已经按照第1章《获取Hadoop并在集群中运行》,并且已经安装Hadoop的。我们将用HADOOP_HOME指代Hadoop的安装目录。
  2. 按照第1章中的说明,《获取Hadoop并在集群中运行》,启动Hadoop。
  3. 本章攻略假设读者知道如何使用Hadoop的处理作业。如果你没有这样做的话,应该遵循第1章《获取Hadoop并在集群中运行》中的《写WordCount MapReduce示例程序,打包并使用独立的Hadoop运行它》攻略。

操作步骤

以下步骤描述了如何运行Adwords示例:

  1. 从http://snap.stanford.edu/data/amazon-meta.html找到亚马逊的产品合作采购网元数据,下载的数据集并将其解压缩。我们称这个文件夹为DATA_DIR
  2. HADOOP_HOME运行以下命令将数据上传到HDFS。如果在/data目录已经存在,它清理干净。此数据集很大,如果你试图用一台计算机上运行它可能需要很长的时间。读者可能希望只上传了第一个50,000行的数据集左右,如果你需要的示例程序快速运行。
$ bin/hadoopdfs -mkdir /data
$ bin/hadoopdfs -mkdir /data/input1
$ bin/hadoopdfs -put /amazon-meta.txt /data/input1
  1. 解压缩适用于第8章的源代码(chapter8.zip)。我们将称该文件夹CHAPTER_8_SRC
  2. 更改CHAPTER_8_SRC/build.xml文件的hadoop.home属性,使其指向Hadoop的安装目录。
  3. CHAPTER_8_SRC目录下运行ant build命令编译源代码。
  4. build/lib/hadoop-cookbook-chapter8.jar复制到HADOOP_HOME
  5. 通过从HADOOP_HOME运行以下命令,提交MapReduce作业。
$ bin/hadoopjar hadoop-cookbook-chapter8.jar chapter8.adwords.ClickRateApproximator/data/input1 /data/output6
  1. 通过运行以下命令,将结果下载到计算机上:
$ bin/hadoopdfs -get /data/output6/part-r-00000clickrate.data
  1. 可以看到该文件包含结果如下。可以使用这些值与贝叶斯分类器的输入进行分类。
keyword:(Annals 74
keyword:(Audio 153
keyword:(BET 95
keyword:(Beat 98
keyword:(Beginners) 429
keyword:(Beginning 110
  1. HADOOP_HOME运行以下命令生成一个出价的数据集。你可以从biddata.data文件找到最终结果。
$ java -cp build/lib/hadoop-cookbook-chapter8.jar chapter8.adwords.AdwordsBidGenerator clickrate.data
  1. 创建一个名为/data/input2目录并上传竞价数据集,并从早期的MapReduce任务结果上传到HDFS的/data/input2目录。
$ bin/hadoopdfs -put clickrate.data /data/input2
$ bin/hadoopdfs -put biddata.data /data/input2
  1. 通过运行第二道MapReduce作业,生成Adwords数据集要使用的数据。
$ bin/hadoopjar hadoop-cookbook-chapter8.jar chapter8.adwords.AdwordsBalanceAlgorithmDataGenerator/data/input2 /data/output7
  1. 通过运行以下命令将结果下载到计算机:
$ bin/hadoopdfs -get /data/output7/part-r-00000adwords.data
  1. 可以看到,将会如下打印结果:
(Animated client23,773.0,5.0,97.0|
(Animated) client33,310.0,8.0,90.0|
(Annals client76,443.0,13.0,74.0|
client51,1951.0,4.0,74.0|
(Beginners) client86,210.0,6.0,429.0|
client6,236.0,5.0,429.0|
(Beginning client31,23.0,10.0,110.0|
  1. 通过运行以下命令为一组随机的关键字进行匹配:
$ javajar hadoop-cookbook-chapter8.jar chapter8.adwords.AdwordsAssigneradwords.data

工作原理

正如我们在工作原理一节中讨论,攻略包含两道MapReduce作业。读者可以从src/chapter8/adwords/ClickRateApproximator.java找到第一道MapReduce作业的源代码。

该mapper函数如下所示。它使用使用亚马逊的数据格式解析亚马逊提供的数据集,并为每个产品标题的每个关键字,输出关键字以及该产品的销售排名。

public void map(Object key, Text value, Context context)
{
    ItemDataitemData = null;
    ListcustomerList =
        AmazonCustomer.parseAItemLine(value.toString());
    if(customerList.size() == 0)
    {
        return;
    }
    for (AmazonCustomer customer : customerList)
    {
        itemData = customer.itemsBrought.iterator().next();
        break;
    }
    String[] tokens = itemData.title.split("\\s");
    for(String token: tokens)
    {
        if(token.length() > 3)
        {
            context.write(new Text(token),
                new IntWritable(itemData.salesrank));
        }
    }
}

然后,由Hadoop的键进行排序所发出的键-值对,并为每个键通过针对该键发出的值列表调用reducer一次。如图所示,在下面的代码中,reducer的计算近似使用对按键发出的销售队列的点击通过率。

public void reduce(Text key, Iterable values,
    Context context) throws IOException, InterruptedException
{
    doubleclickrate = 0;
    for(IntWritableval: values)
    {
        if(val.get() > 1)
        {
            clickrate = clickrate + 1000/Math.log(val.get());
        }
        else
        {
            clickrate = clickrate + 1000;
        }
    }
    context.write(new Text("keyword:" +key.toString()),
    newIntWritable((int)clickrate));
}

暂时没有公开可用的竞价数据集。因此,我们将生成一个随机的出价数据使用本章攻略配置的AdwordsBidGenerator。它将读出由先前的配方所生成的关键字,并生成一个随机的出价数据集。

然后,我们将使用第二道MapReduce作业合并出价数据集的点击率,并生成具有投标“信息分类对关键字的数据集。可以从src/chapter8/adwords/AdwordsBalanceAlgorithmDataGenerator.java找到第二道MapReduce作业的源代码。Mapper功能如下所示:

public void map(Object key, Text value, Context context)
    throws IOException, InterruptedException
{
    String[] keyVal = value.toString().split("\\s");
    if (keyVal[0].startsWith("keyword:"))
    {
        context.write(
            new Text(keyVal[0].replace("keyword:", "")),
            new Text(keyVal[1]));
    }
    else if (keyVal[0].startsWith("client"))
    {
        List bids = new ArrayList();
        double budget = 0;
        String clientid = keyVal[0];
        String[] tokens = keyVal[1].split(",");
        for (String token : tokens)
        {
            String[] kp = token.split("=");
            if (kp[0].equals("budget"))
            {
                budget = Double.parseDouble(kp[1]);
            }
            else if (kp[0].equals("bid"))
            {
                String[] bidData = kp[1].split("\\|");
                bids.add(bidData);
            }
        }
        for (String[] bid : bids)
        {
            String keyword = bid[0];
            String bidValue = bid[1];
            context.write(new Text(keyword),
                new Text(new StringBuffer()
                .append(clientid).append(",")
                .append(budget).append(",")
                .append(bidValue).toString()));
        }
    }
}

该map函数读取两个投标的数据集和点击通过率数据集,输出这两种类型作为关键字的数据。然后,每个reducer为每个关键字收到相应的所有投标和相关的点击数据。然后reducer合并的数据,并发出针对每个关键字出价的列表。

public void reduce(Text key, Iterable values,
    Context context) throws IOException, InterruptedException
{
    String clientid = null;
    String budget = null;
    String bid = null;
    String clickRate = null;
    Listbids = new ArrayList();
    for (Text val : values)
    {
        if (val.toString().indexOf(",") > 0)
        {
            bids.add(val.toString());
        }
        else
        {
            clickRate = val.toString();
        }
    }
    StringBufferbuf = new StringBuffer();
    for (String bidData : bids)
    {
        String[] vals = bidData.split(",");
        clientid = vals[0];
        budget = vals[1];
        bid = vals[2];
        buf.append(clientid).append(",")
            .append(budget).append(",")
            .append(Double.valueOf(bid)).append(",")
            .append(Math.max(1, Double.valueOf(clickRate)));
        buf.append("|");
    }
    if (bids.size() > 0)
    {
        context.write(key, new Text(buf.toString()));
    }
}

最后,Adwords分配器加载投标数据和存储他们对应内存中的关键字。给定一个关键字时,AdWords分配器发现具有下列公式最大值和选择所有的投标广告中的投标:

Measure = bid value * click-through rate * (1-e^(-1*current budget/ initial budget))

更多参考

上述配方假设Adwords分配器可以加载所有的数据进入内存中,并进行广告分配的决策。但是,如果数据集很大,我们可以通过关键字的数据集划分在多台计算机(例如,分配字母“A-D”开头的关键字到第一台计算机等)。

关于在线广告的更多信息,可以发现从Anand Rajaraman和Jeffrey D. Ullman编写的《Mining of Massive Datasets》一书找到。这本书可以从http://infolab.stanford.edu/~ullman/mmds.html找到。

你可能感兴趣的:(使用AdWords平衡算法给广告分配关键字)