计算广告在线产品逻辑-计算广告3-8章
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了计算广告在线产品逻辑-计算广告3-8章相关的知识,希望对你有一定的参考价值。
参考技术A之前跳读了技术相关的计算广告后几章,感觉还是很缺乏对业务的理解,所以跳回来一次读完了广告业产品业务相关的几章,这里来做一次总结,无论是广告产品经理亦或是广告业务相关算法和开发人员,都建议可以读读。
由于之后会涉及到相当多的业务逻辑和产品业务,这里首先做一个总结,之后也按照这条主线顺序进行说明。
广告产品由于数据利用和变现需求的推动,一般将这一发展历程分为四个阶段。
计算广告产品发展历程如下图所示。
随着受众定向技术的发展,广告位独占式售卖的执行方式也发生了很大的变化。即使某个广告位全部投放一个广告主的创意,也并不意味着一定要投放同样的一款创意,而受众定向在其中也可以起到很重要的作用。例如,某汽车生产商广告主旗下可能有多个系列的产品,如小型车、紧凑型车、豪华车、SUV等,而这些车型的潜在购买人群其实也有很大的区别,如果能够对这些系列的受众分别投送相应的创意,就可以取得更好的效果。另外,即使在受众上无法区分的情形下,也可以利用频次控制的方式向同一用户递进式地展示一系列创意,以达到更好的效果。
广告位合约还有一种变形的形式,即按照广告位的轮播售卖。
受众定向方法概览
受众定向标签体系
虽然从交易模式上来看,展示量合约仍然是比较传统的交易模式,但是从技术层面上看,这种模式的出现实际上已经反映了互联网广告计算驱动的本质:分析得到用户和上下文的属性,并由服务端根据这些属性及广告库情况动态决定广告候选。这一商业模式的出现,需要有一系列技术手段的支持,这些手段主要包括受众定向、流量预测和担保式投放等。受众定向已经在刚才部分进行了介绍,这里讨论一下流量预测和在线分配的产品策略问题。
流量预测
流量预测在广告产品中包括以下三个主要用途。
(1)售前指导。在展示量合约广告中,由于要约定曝光总数,事先尽可能准确地预测各人群标签的流量变得非常关键。
(2)在线流量分配。当一次曝光同时满足两个以上合约的要求时,怎样决策将它分配给哪个合约以达到整体满足所有合约的目的。各种在线分配算法都要依赖流量预估的结果,以达到高效和准确的目标。
(3)出价指导。在竞价广告中,由于没有量的保证,广告主需要根据自己预计的出价先了解一下可能获得多少流量,以判断自己的出价是否合理。
在线分配
展示量合约这种保量合约都面临一个问题:各个合约要求的人群很可能大量交叠,如何设计分配策略,使得各个合约都尽可能被满足。对这一策略问题,将其简化为一个二部图(bipartite graph)匹配的问题。二部图的一方是表示广告库存的供给节点,每个节点代表的是所有人群标签都相同的广告流量集合;二部图的另一方是表示广告合约的需求节点,每个节点代表的是一个广告合约的人群标签条件。在线分配问题实际上是一个二部图最佳分配问题。
竞价、类搜索的广告投放架构都是从搜索广告发展起来的。因此,这里首先对搜索广告进行介绍,这也是整个在线广告市场中市场份额最大的类型。
搜索广告创意的展示区域一般来说分为北(north)、东(east)、南(south)三个部分。
在互联网广告的整个产品谱系当中,搜索广告有着特殊重要的地位,具有以下鲜明的产品和技术特点。
(1)搜索广告的变现能力,即 eCPM远远高于一般的展示广告,其市场重要程度也就得以彰显。
(2)搜索广告的受众定向标签,即是上下文的搜索查询。由于搜索词非常强地表征着用户的意图,搜索广告可以进行非常精准的定向。相对这样的上下文信息,根据用户历史行为得到的兴趣标签的重要性大打折扣,这一方面是因为其信号远不如搜索词强烈,另一方面是因为用户这样明确意图的任务是决不能被打断的(参见第2章广告有效性原理部分)。因此,搜索广告里的eCPM由一般情形下的r(a,u,c)退化成了r(a,c)。
搜索广告从一开始就具有原生广告的特点:它的商业化结果与自然结果一样,由用户的主动意图触发,并且展示形式上与自然结果相差不大。这里放上原生的搜索广告结果样例,之后可以结合原生广告章节进行进一步理解。
除了产品形式上的创新,搜索广告的投放和优化策略也是产品的重要一环。搜索广告的整个决策过程可以分为 查询扩展、检索、排序、放置、定价 等几个阶段。
查询扩展是搜索广告独有的策略,目的是给广告主自动地拓展相关的查询词,扩大采买流量;
广告检索和将候选广告根据 eCPM 排序是广告系统较为通用的核心流程(本章后面介绍广告网络时再讨论);
定价是竞价广告非常核心的策略(在下一节机制设计中将重点介绍)。
查询扩展相关的方法有
当广告候选完成排序以后,需要分别确定北区和东区的广告条数,这个环节称为广告放置(ad placement)。
在线广告竞价市场最常见的定价策略是 GSP方案;另外有一种 VCG(Vickrey-Clarke-Groves)定价策略,虽然理论上比GSP更合理,但是由于原理较复杂,向广告主解释起来有难度,因此在实际系统中采用的并不多。下面分别介绍这两种定价策略。
1.广义第二高价(GSP)
所谓第二高价,指的是在只有一个位置的拍卖中,向赢得该位置的广告主收取其下一位广告主的出价,这样的拍卖也叫作Vickrey拍卖。很容易直觉地将第二高价策略推广成下面的策略:对赢得每一个位置的广告主,都按照他下一位的广告位置出价来收取费用,这就是广义第二高价。
2.VCG
其基本思想是:对于赢得了某个位置的广告主,其所付出的成本应该等于他占据这个位置给其他市场参与者带来的价值损害。
由于实际使用不多,这里不对VCG广告工作原理和逻辑进行说明。
为了控制广告的质量和保持一定的出售单价,竞价广告市场往往要设置一个赢得拍卖位置的最低价格,这一价格我们称为市场保留价(Market Reserve Price,MRP),俗称“起价”或“底价”。广告主的出价只有在高于市场保留价时才能获得竞价机会,同时在赢得某个拍卖位置后,如果根据定价策略算出的付费低于市场保留价(以广义第二高价为例,很容易验证这种情况是可能发生的),也需要调整到市场保留价的水平上。
在 CPC 结算的广告产品中,eCPM 可以表示成点击率和出价的乘积,即 r=µ·ν=µ·bid CPC 。但是在竞价的机制设计中,有时会对此公式做一些微调,把它变成下面的形式:
其中的κ为一个大于0的实数。可以考虑两种极端情况来理解κ的作用:当κ→∞时,相当于只根据点击率来排序而不考虑出价的作用;反之,当κ→0时,则相当于只根据出价来排序。因此,随着κ的增大,相当于我们在挤压出价在整个竞价体系中的作用,因此我们把这个因子叫做价格挤压(squashing)因子。
价格挤压因子的作用主要是能够根据市场情况更主动地影响竞价体系向着需要的方向发展。比如说,如果发现市场上存在大量的出价较高但品质不高的广告主,则可以通过调高κ来强调质量和用户反馈的影响;如果发现市场的竞价激烈程度不够,则可以通过降低κ来鼓励竞争。
竞价广告网络的售卖的标的主要是人群,而广告位被淡化了。(合约广告是很难淡化广告位标的的。)另外,当流量满足多个广告活动要求时,简单地采用竞价模式而不用考虑量的合约。
广告网络中的广告决策过程与搜索广告相比,整个流程要简单一些,主要分为检索、排序、定价等几个阶段。这里和具体的推荐过程有一定程度的类似。
1.广告检索
广告与搜索面对的文档其实不同,它往往是一个用布尔表达式表达的投放条件,而不是可以简单看成一个词的集合。搜索那样的面向词集合的检索方案对布尔表达式来说不是最有效的。
搜索广告检索与搜索基本一致,用常规的倒排索引技术就可以解决。展示广告网络与搜索广告不同,由于用户意图不明确,往往要将更多的关键字、兴趣标签同时用于检索过程。这里可以类比推荐系统中的召回阶段。
2.广告排序
竞价广告中排序的准则是eCPM,而在CPC结算的情形下,对eCPM的估计转化为对点击率的估计问题。这里同样可以类比目标有修改的推荐系统排序阶段。
在了解了竞价、合约这两类主要的广告交易方式后,这里简要对比一下它们的优缺点。
从供给方或广告市场方来看,合约广告和竞价广告的对比可以类比于计划经济和市场经济的区别。在合约广告的情况下,所有量的保证和质的优化都是由媒体方的广告投放机来统一完成,而在竞价广告的情况下,市场只负责制定竞价和收费的规则,而各广告主量的保证完全采用市场竞争的方式来完成。在这种情况下,市场方需要仔细设计宏观竞争机制,但是不一定需要实现象合约广告那样的交易级别的计划调度。
在竞价广告中,供给方和广告主的约定比较松散:首先,供给方不再向广告主承诺广告投放量;与此相对应,点击单价由广告主自行决定。这样的交易逻辑使得广告合同由首先确保量的结构变成了首先确保单位成本的结构。这实际上是非常革命性的变化,它使得广告市场产生了以下三个有利于大幅提高广告效果的发展趋势。
(1)非常精细的受众定向可以被无障碍地使用在交易中,而这是展示量合约广告很难做到的。
(2)大量的中小广告主逐渐成为参与竞价的主体,这使得市场的规模得到了快速扩张。
(3)与合约广告相比,竞价广告中数据的价值得以彰显,整个市场开始以数据为核心来组织和运营广告产品。
RTB 的产生,使得广告市场向着透明的比价平台的方向发展,这样的平台就是广告交易平台,即ADX,其主要特征即是用RTB的方式实时得到广告候选,并按照其出价简单完成投放决策。
竞价广告网络中的受众定向虽然可以很精准,但是还是会有一些完成不了的场景。例如,某广告主希望对自己的流失用户进行一次广告促销,或某广告主希望广告平台帮助找到与其用户类似的潜在用户。很显然,无论怎样选择在广告网络中的人群标签,都不可能直接完成上述的任务。实际上,这两个任务有一个共同的特点,即我们在加工人群标签的过程中需要利用到广告主的数据。这样的标签称为定制化用户标签(customized audience segmentation)。
因此,采用广告网络这样的封闭式竞价方案是无法规模化和精细化地针对定制化标签进行投放的。什么样的解决方案才能够规模化呢?其实很简单,只要把竞价过程开放,在广告展示时由需求方来判断是否需要并出价,就可以解决上面的问题,这样的思路就产生了实时竞价。
实时竞价的接口可以分成两个过程,即预先进行的将 ADX与 DSP的用户标识对应起来的 cookie映射(cookie mapping)过程和线上广告请求时的竞价和投放过程。
以Web投放环境为例,RTB的广告请求可以分为以下三个步骤。
3.2 其他程序化交易方式
优选比实时竞价产生要早,可以看成是只有一个需求方的程序化交易,优选方式允许单个需求方按照自己的意愿来挑选流量,但是又可以避免复杂的竞价过程。由于只有一个需求方参与,媒体可以比较容易地对广告的质量和来源进行控制。这种交易一般按照CPM 方式结算,由于没有了多方竞价,又有选择流量的便利,往往要约定一个比市场价格更高的 CPM单价。
除了实时竞价这种公开的市场拍卖机制以外,有时媒体为了保证广告主的质量,希望将拍卖限制在一些被邀请需求方的小范围内。这种程序化交易叫作私有市场。私有市场中的在线交易过程与公开的实时竞价一致。
私有市场可以说兼顾了优选与实时竞价的好处。
ADX的产品策略较为简单,由于所有的广告竞价都是实时进行,因此不需要保存广告库,因而也不需要广告检索流程,排序过程也非常简单。ADX 一般为 CPM 结算方式。
与 ADX相对应,以 RTB方式购买广告的产品形态就是需求方平台,即 DSP。这一产品的核心特征有两个:一个是RTB方式的流量购买,另一个是需要支持需求方定制化的用户划分。
DSP的广告决策过程(如图6-5所示)与广告网络非常相似,同样先要经过检索、排序、定价几个阶段,主要的差别是完成广告选择后,又增加了出价的步骤。而出价正是 DSP的关键产品策略之一,因为在实时竞价环境中,出价直接决定着DSP的流量基本单位成本,也就决定着利润。这里着重要注意的限制条件是 预算 。
与需求方平台相关的技术有重定向和新客推荐,相关业务逻辑成因已在上文说明,这里不再阐述。
对于媒体而言,无需把全部流量的变现都放在一种交易方式上。媒体既可以通过直接销售来高溢价地售卖品牌广告,也可以综合使用各种程序交易方式以追求更高的eCPM。
媒体的统一变现平台需要这样的逻辑。当广告请求到达时,首先检查优先销售的订单有无需求,这包括CPT和CPM的合约。如果有需求,按照优先级和在线分配的方案完成投放;如果没有这类销售合约,则进入竞价流程。竞价时,从自运营广告主库中找出eCPM较高的,并估算可供调用的若干广告网络的eCPM,在这两者之间找到较高的广告候选,再以此作为MRP,通过RTB接口向接入的各DSP实时询价。可以看出,在这样的逻辑中,广告请求是被分配到自运营广告库,还是其他广告网络,或者是DSP,是根据他们的收益在线动态决定的,这样的方案称为动态分配(dynamic allocation)。对应的产品形态就叫作供给方平台(Supply Side Platform,SSP)。
一般将对精准广告业务有直接贡献的数据分为这几类。
广告中用到的用户数据,根据其来源的不同可以分为第一方数据、第二方数据和第三方数据。第一方和第二方分别是指广告主和广告平台,而不直接参与广告交易的其他数据提供方统称为第三方。在广告网络中,主要使用第二方数据指导广告投放;而在实时竞价环境下,不仅第一方数据可以被利用,大量第三方数据的加工和交易也逐渐发展起来。
第一方数据的收集和加工是广告市场上非常重要的环节。不过对于没有这方面技术积累的广告主而言,专门设团队进行数据加工是没有必要的。因此,市场上也产生了专从事此业务的产品,称为数据管理平台即DMP。DMP有下面几个核心的产品功能。
(1)它可以为网站(可以是媒体也可以是广告主网站)提供受众定向功能,并将得到的用户标签应用于网站业务。
(2)如果媒体网站授权,DMP可以提供接口对加工出来的用户标签进行变现,并与网站进行分成。
(3)广告主网站可以通过 DMP 与广告采买渠道进行更方便的数据对接。
数据交易平台(data exchange)的主要产品功能是聚合各种来源的在线的用户行为数据,加工成有价值的用户标签,然后在广告市场上通过售卖这些标签来变现。数据交易平台与数据管理平台的产品边界并不是泾渭分明。一般来说,数据交易平台除了聚合成型的用户标签,也都会提供聚合原始行为数据自行加工标签的功能,也就是兼具 DMP的产品功能。
可以认为DMP是站在第一方数据的角度提供产品,而数据交易平台主要是站在第三方数据的角度提供产品。
以下对多种形式的原生广告进行梳理。
这里对移动广告的创意形式做一个简要的归纳。
在这个基础上就可以给出一定的原生广告的展现形式,即植入式广告,这里以下图作为说明,希望读者可以结合自身体会对其进行理解。
最后这里再重复一遍。
计算广告
GBDT+LR
GBDT+LR是一种融合梯度提升决策树(Gradient Boosting Decision Tree,简称GBDT)和逻辑回归(Logistic Regression,简称LR)的机器学习方法。这种方法的主要目的是充分利用GBDT和LR的优势,提高模型的预测性能。
GBDT(Gradient Boosting Decision Tree):
GBDT是一种基于梯度提升(Gradient Boosting)的集成学习方法。GBDT通过迭代训练多个决策树模型,并将它们组合在一起,形成一个强大的预测模型。在每一轮迭代中,GBDT会训练一个新的决策树,该决策树试图纠正前面已训练的决策树的预测误差。通过这种方式,GBDT能够不断提高模型的预测能力。
LR(Logistic Regression):
逻辑回归是一种广泛应用于分类问题的线性模型。LR通过使用对数几率函数(logistic function)将线性回归的输出映射到概率空间,从而得到属于某一类别的概率。然后,可以根据概率阈值对样本进行分类。
GBDT+LR的融合方法:
GBDT+LR方法的核心思想是先使用GBDT对原始特征进行特征转换,然后将转换后的特征输入到LR模型中进行分类。
具体步骤如下:
(1)使用GBDT训练模型:首先,使用GBDT对训练数据进行训练,得到多个决策树模型。
(2)特征转换:将训练数据通过GBDT模型,得到每个样本在每棵树上的叶子节点。这些叶子节点可以看作是原始特征的高阶组合。将叶子节点编码成一种称为“one-hot encoding”的形式。这样,每个样本就会被转换成一个高维稀疏向量,向量中的每个元素代表该样本在某个叶子节点上的取值。
(3)训练LR模型:使用转换后的特征作为输入,训练逻辑回归模型。由于特征已经经过了GBDT的处理,这个过程可以看作是在学习GBDT所捕捉到的特征组合之间的线性关系。
(4)预测:对于新的样本,首先将其通过GBDT进行特征转换,然后将转换后的特征输入到LR模型中,得到分类结果。
GBDT+LR的优势:
特征工程:GBDT可以自动学习到有效的特征组合,降低了手动进行特征工程的难度和工作量。
稀疏特征处理:经过GBDT特征转换后,特征变得稀疏且高维,逻辑回归对稀疏特征的处理能力较强,有助于提高模型性能。
优化模型性能:GBDT和LR的组合可以充分利用非线性和线性模型的优势,提高模型的预测性能。
应用场景:
GBDT+LR模型适用于各种分类问题,特别是在广告点击率预测、金融风险控制、推荐系统等领域有着广泛的应用。
总结:
GBDT+LR是一种结合了梯度提升决策树和逻辑回归的机器学习方法。通过使用GBDT进行特征转换和逻辑回归进行分类,该方法充分发挥了两者的优势,提高了模型的预测性能。GBDT+LR模型在各种分类问题中均有良好的表现,尤其适用于广告点击率预测、金融风险控制和推荐系统等
单独的使用GBDT模型,容易出现过拟合,在实际应用中往往使用 GBDT+LR的方式做模型训练
首先根据样本训练出GBDT树,对于每个叶子节点,回溯到根节点都可以得到一组组合特征,所以用叶子节点的标号可以代表一个新的组合特征。结合上面的图,用一个样本为例,直观的表达如下
编辑切换为居中
添加图片注释,不超过 140 字(可选)
其中 0号 组合特征的含义是:ageLessThan15AndIsMale,该样本取值 0
其中 1号 组合特征的含义是:ageLessThan15AndIsNotMale,该样本取值 1
其中 2号 组合特征的含义是:ageLargerOrEqualThan15,该样本取值 0
其中 3号 组合特征的含义是:useComputerDaily,该样本取值 0
其中 4号 组合特征的含义是:notUseComputerDaily,该样本取值 1
这部分特征是GBDT生成的组合特征,再结合LR固有的稀疏特征,就组成了 GBDT + LR 模型。
生成样本向量阶段,样本首先过GBDT模型,生成组合特征部分的输入向量,再结合固有的稀疏特征向量,组成新的特征向量,再以它训练LR,示例如下:
Sklearn
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.linear_model import LogisticRegression
from sklearn.metrics import roc_auc_score
# 生成模拟数据的函数
def generate_data(n_samples=10000, n_features=10, random_state=None):
np.random.seed(random_state)
X = np.random.rand(n_samples, n_features)
y = (np.random.rand(n_samples) > 0.5).astype(int)
return pd.DataFrame(X, columns=[f'feature_i' for i in range(n_features)]), y
# 生成模拟数据
X, y = generate_data(n_samples=10000, n_features=10, random_state=42)
# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练GBDT模型
gbdt = GradientBoostingClassifier(n_estimators=50, random_state=42)
gbdt.fit(X_train, y_train)
# 对训练数据进行GBDT特征变换
train_leaves = gbdt.apply(X_train)[:, :, 0]
test_leaves = gbdt.apply(X_test)[:, :, 0]
# 对GBDT生成的叶子节点特征进行One-Hot编码
encoder = OneHotEncoder()
encoder.fit(train_leaves)
train_leaves_encoded = encoder.transform(train_leaves)
test_leaves_encoded = encoder.transform(test_leaves)
# 训练LR模型
lr = LogisticRegression(solver='lbfgs', max_iter=1000, random_state=42)
lr.fit(train_leaves_encoded, y_train)
# 对测试集进行预测并计算AUC
y_pred = lr.predict_proba(test_leaves_encoded)[:, 1]
auc = roc_auc_score(y_test, y_pred)
print(f'Test AUC: auc:.4f')
Sklearn
import numpy as np
import pandas as pd
from sklearn.model_selection import train_test_split
from sklearn.preprocessing import OneHotEncoder
from sklearn.ensemble import GradientBoostingClassifier
from sklearn.metrics import roc_auc_score
# 生成模拟数据的函数
def generate_data(n_samples=10000, n_features=10, random_state=None):
np.random.seed(random_state)
X = np.random.rand(n_samples, n_features)
y = (np.random.rand(n_samples) > 0.5).astype(int)
return pd.DataFrame(X, columns=[f'feature_i' for i in range(n_features)]), y
# 逻辑回归底层实现
class LogisticRegression:
def __init__(self, learning_rate=0.01, epochs=10, batch_size=32):
self.learning_rate = learning_rate
self.epochs = epochs
self.batch_size = batch_size
def _sigmoid(self, x):
return 1 / (1 + np.exp(-x))
def fit(self, X, y):
self.w = np.random.rand(X.shape[1] + 1)
X = np.c_[np.ones(X.shape[0]), X]
for epoch in range(self.epochs):
for i in range(0, len(X), self.batch_size):
X_batch = X[i:i + self.batch_size]
y_batch = y[i:i + self.batch_size]
y_pred = self._sigmoid(np.dot(X_batch, self.w))
gradient = np.dot(X_batch.T, y_pred - y_batch) / len(y_batch)
self.w -= self.learning_rate * gradient
def predict_proba(self, X):
X = np.c_[np.ones(X.shape[0]), X]
return self._sigmoid(np.dot(X, self.w))
# 生成模拟数据
X, y = generate_data(n_samples=10000, n_features=10, random_state=42)
# 划分数据集为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
# 训练GBDT模型
gbdt = GradientBoostingClassifier(n_estimators=50, random_state=42)
gbdt.fit(X_train, y_train)
# 对训练数据进行GBDT特征变换
train_leaves = gbdt.apply(X_train)[:, :, 0]
test_leaves = gbdt.apply(X_test)[:, :, 0]
# 对GBDT生成的叶子节点特征进行One-Hot编码
encoder = OneHotEncoder()
encoder.fit(train_leaves)
train_leaves_encoded = encoder.transform(train_leaves).toarray()
test_leaves_encoded = encoder.transform(test_leaves).toarray()
# 训练自定义逻辑回归模型
lr = LogisticRegression(learning_rate=0.01, epochs=10, batch_size=32)
lr.fit(train_leaves_encoded, y_train)
# 对测试集进行预测并计算AUC
y_pred_proba = lr.predict_proba(test_leaves_encoded)
auc = roc_auc_score(y_test, y_pred_proba)
print(f'Test AUC: auc:.4f')
Pyspark
import numpy as np
from pyspark.ml import Pipeline
from pyspark.ml.classification import LogisticRegression
from pyspark.ml.feature import VectorAssembler
from pyspark.ml.regression import GBTRegressor
from pyspark.sql import SparkSession
from pyspark.sql.functions import col
# 数据生成函数
def generate_data(n_samples=10000, n_features=20, random_state=None):
if random_state:
np.random.seed(random_state)
X = np.random.rand(n_samples, n_features)
w = np.random.rand(n_features)
b = np.random.rand()
y = np.dot(X, w) + b
y = 1 / (1 + np.exp(-y))
y = np.round(y)
return X, y
# 生成数据
n_samples = 10000
n_features = 20
X, y = generate_data(n_samples=n_samples, n_features=n_features, random_state=42)
# 创建Spark会话
spark = SparkSession.builder.appName("GBDT_LR_Example").getOrCreate()
# 将数据转换为Spark DataFrame
data = np.column_stack((X, y))
data = spark.createDataFrame(data.tolist(), ["feature_".format(i) for 计算广告基础