将机器学习应用于营销支出
本文我们将研究将机器学习应用于营销支出。我们将研究使用营销来预测销售,以及衡量不同营销活动的影响。
学习目标
这篇文章将给出回归,特征工程和使用神经网络建模数据集的真实示例。
问题设定
在这个问题上,我们是一个模拟营销组织。我们会查看历史支出和销售额,并将每日营销支出与日常销售情况相对应。
我们的环境简化为适合作为入门机器学习的学习工具。在这种情况下,我们有很多天的品牌销售,但每天品牌的销售仅取决于公司当天的支出。
在这个例子中,有两种类型的营销支出,品牌营销(brand)来创建品牌形象,直接面向消费者的营销(d2c)来推动销售。
我们根据两种类型的营销广告与销售相关的规则来模拟此数据,但如果两次花费之间存在平衡,则创建的销售额最多。支出与销售额的图表如下:
单个结果可以像excel表一样可视化如下:
方法摘要
要了解营销支出的影响,我们将首先使用线性回归将营销支出与销售进行对比。然后我们查看损失曲线并识别线性回归的不足,鼓励我们进行特征工程。
在手动特征工程之后,为了帮助完成特征生成过程,我们使用隐藏的神经网络层来提取特征,并将它们用于我们的最终回归问题。我们将比较和对比解释性和性能。
在整个过程中,我们将显示输出图形和结果,并在末尾给出Python代码。
线性回归建模
要了解销售和营销支出之间的关系,我们首先拟合线性回归模型。
我们的线性回归近似于此曲线
sales = 5.11 * brand_marketing + 3.28 *d2c - 2.45
我们可以直接读取每一种支出类型的ROI,除了我们可以看到下面的模型不能准确地表示数据。模型学到的预测用蓝色表示,显然不符合红色表示的销售情况:
线性回归估计与销售额
误差量化
为了总结我们模型的准确性,我们来看看真实销售和我们的预测之间的平方误差。我们采用这些平方误差的平均值来表示我们的表现。
为了确保计算出真实的性能度量,我们在模型没有训练的数据点上测量我们的损失。这接近于衡量模型在未来看不见的数据上的表现。
使用线性回归,我们的误差为0.89。这没有意义,但与其他模型相比,以后会更有意义。
但是,我们可以清楚地看到我们的模型没有准确地捕捉销售趋势,因为我们看到预测与真实销售之间存在可预测差异的区域。
特征工程
作为一名聪明的专业人士,我们得出的结论是,仅从支出上进行线性回归是不够的。为了扩展我们的机器学习模型,当两个输入都很大时,我们注意到显著的增长,所以也许我们需要一个特征来捕获这两种开销类型的组合。因此,我们产生了一个特征,即brand *d2c支出。这个特征要高,就要求brand 和d2c都要高。
现在,我们的模型学会了更好的近似曲线,用函数:
销售额= 0.68 *brand - 1.01 * d2c + 8.74 *brand * d2c - 0.29
特征工程近似
这看起来不错。我们的预测现在能更好地跟踪销售情况,但我们仍不完美。误差从纯线性回归的.9降到了0.2。
看看我们的参数,我们可以看到brand 和d2c营销的结合是最强大的影响者,系数为8.74。这只是真实销售额的近似值,但该参数可以解释为:如果brand 支出为0,则增加d2c支出将无效,因为brand * d2c将为0。
如果brand支出满1(千美元),那么随着我们增加d2c支出,我们将获得每花费8.74美元的投资回报率。
这听起来并不合理,但作为近似值,它告诉我们增加销售的最佳方法是在brand 和d2c营销之间取得平衡,因为组合是最大的因素。
在这个时候,我们认识到我们仍然有0.2的显着损失,并且仍然有可预测错误的可见区域,并且认为我们仍然可以更多地改进我们的模型。
用于特征提取的神经网络
为了添加更多生成的特征,而无需定义它们应该是什么,我们考虑一个生成6个特征的小型神经网络,然后使用这些特征来预测销售额。
在训练我们的神经网络之后,我们检查模型的特征:
feature 0 = 2.85 * brand + 1.83 * d2c -2.79
feature 1 = 1.62 * brand -2.15 * d2c -0.03
feature 2 = 0.08 * brand -0.54 * d2c -0.77
feature 3 = 0.55 * brand + 0.16 * d2c -0.81
feature 4 = 3.83 * brand + 2.27 * d2c + 0.86
feature 5 = 1.6 * brand + 1.78 * d2c + 0.7
然后,这些特性被clipped ,以将任何负值设置为零,然后使用以下方法预测yhat, feats表示对应特征的clipped 版本:
yhat = 3.0 * feat 0 -1.48 * feat 1 + 0.82 * feat 2 -0.2 * feat 3 + 1.6 *feat 4 -1.82 *feat 5 -0.56
哇,这么难解释!在后面的课程中,我们会讲到神经网络的解释,但我们肯定不会去读这些单独的特征,那太乏味了。
但是看看我们的损失,我们的损失下降到了0.12!此外,我们的图表现在看起来非常接近真实的销售情况。
神经网络逼近
深度神经网络
最后,让我们假设我们并不是要尝试衡量投资回报率,以便为营销决策提供信息,而只是尝试预测每日销售额。我们不是使用神经网络中的6个隐藏单元,而是使用100个隐藏单元,并使用该隐藏层计算100个单元的新隐藏层,我们最终用它们执行线性回归。
这进一步减少了我们的损失,现在我们几乎完全拟合数据集,损失0.001。现在想想,这甚至不是训练数据。这不是模型适合它所训练的数据的程度,这就是模型与训练期间从未见过的数据相符的程度。
但是,如果我们想解释这个模型怎么办?我们有一个100单元的隐藏层,然后是另一个100单元的隐藏层。因此,为了计算第二隐藏层,我们从第一隐藏层为第二隐藏层中的每100个单元计算100次乘法。这是它学到的10,000次乘法!我们永远无法概念化许多交互,但我们认识到神经网络已经开发了这些功能以准确地拟合数据。
Wrapping Up
让我们回顾一下。初始线性回归模拟了数据集,但它显然具有不适合的区域。我们进行了一些特征工程,因为我们从思考问题中猜到了一些有价值的特征。这有助于我们更好地拟合数据集,我们的模型更准确但有点难以理解。
接下来,我们在网络中引入了一个隐藏层来学习我们的特征,我们提出了六个特征,以某种方式解决了我们的问题。这些变得更难以解释,但我们知道这些特征在某种程度上比我们原来的两三个更好地理解模型。
我们让深层神经网络解决问题。我们观察到这个模型更加准确,但是解释数万次乘法是不可行的。
Python代码分析
首先,导入Python库
import numpy as np import pandas as pd import matplotlib %matplotlib notebook import matplotlib.pyplot as plt from sklearn.neural_network import MLPRegressor from sklearn.linear_model import Ridge from mpl_toolkits.mplot3d import Axes3D
我们设置了一个随机种子,这样如果你在本地机器上执行此操作,你会看到与我们在这里看到的相同的随机结果。这对于再现性很重要
np.random.seed(0)
接下来我们生成数据集,Python代码如下:
NUM_SAMPLES = int(2e3) brand_spend = np.random.rand(NUM_SAMPLES) d2c_spend = np.random.rand(NUM_SAMPLES) individual_contributions = brand_spend * .01 + d2c_spend * .02 collaboration = np.square(brand_spend) * d2c_spend * 10 sales = individual_contributions + collaboration + np.random.rand(NUM_SAMPLES)/10
最后,对于线性回归部分,我们有一个执行线性回归的函数和另一个函数来绘制结果:
def model_sales_regression(dataset, model='Ridge'): num_samples = dataset.shape[0] cutoff = (num_samples * 3) // 4 Xtrn = dataset.drop('sales', 1).iloc[:cutoff,:] Ytrn = dataset['sales'].iloc[:cutoff] Xval = dataset.drop('sales', 1).iloc[cutoff:,:] Yval = dataset['sales'].iloc[cutoff:] model = Ridge().fit(Xtrn, Ytrn) coefs = model.coef_.round(2) yhat = model.predict(dataset.drop('sales', 1)) yhatval = model.predict(Xval) loss = np.square(Yval - yhatval).mean() print('learned coefficients', list(zip(X.columns, coefs))) print('loss:', loss) print('intercept', model.intercept_.round(2)) return model, yhat, coefs, loss def graph_real_and_predicted(dataset, yhat, fname=None): X = dataset.drop('sales', 1) Y = dataset['sales'] X1 = X['brand'] X2 = X['d2c'] fig = plt.figure(dpi=80, figsize=(10, 4)) ax = fig.add_subplot(111, projection='3d') ax.scatter(X1, X2, Y, c='r', label='real sales', s = 1) ax.scatter(X1, X2, yhat, c='b', label='estimated sales', s = 1) ax.set_xlabel('Brand Marketing') ax.set_ylabel('Direct to Consumer') ax.set_zlabel('Sales') plt.legend() if fname is not None: plt.savefig('images/' + fname + '.jpg')
在这里,我们将数据集包装到DataFrame中。这让我们可以像python中的excel表一样查看数据集。
dataset = pd.DataFrame({ 'brand': brand_spend, 'd2c': d2c_spend, 'sales': sales }).round(5)
查看的Python代码如下:
dataset.head()
接下来,我们将数据集拆分为X和Y,并可视化值。
X = dataset.drop('sales', 1) Y = dataset['sales'] X1 = X['brand'] X2 = X['d2c'] fig = plt.figure(dpi=80, figsize = (10, 4)) ax = fig.add_subplot(111, projection='3d') ax.scatter(X1, X2, Y, c='r', label='real sales', s = 1) ax.set_xlabel('Brand Marketing') ax.set_ylabel('Direct to Consumer') ax.set_zlabel('Sales') plt.legend() plt.savefig('images/data.jpg')
这是训练线性回归的Python代码
model, yhat, coefs, loss = model_sales_regression(dataset) graph_real_and_predicted(dataset, yhat, 'linear')
接下来,我们添加一个特征并从增强数据集执行线性回归。
dataset['brand * d2c'] = dataset['brand'] * dataset['d2c'] model, yhat, coefs, loss = model_sales_regression(dataset) graph_real_and_predicted(dataset, yhat, 'feature_engineering')
使用神经网络,我们定义了我们的多层感知器函数。
def model_sales_MLP(dataset, hidden, print_coefs = True, max_iter= 10000): num_samples = dataset.shape[0] cutoff = (num_samples * 3) // 4 Xtrn = dataset.drop('sales', 1).iloc[:cutoff,:] Ytrn = dataset['sales'].iloc[:cutoff] Xval = dataset.drop('sales', 1).iloc[cutoff:,:] Yval = dataset['sales'].iloc[cutoff:] model = MLPRegressor(hidden, validation_fraction = 0, solver='lbfgs', max_iter= max_iter).fit(Xtrn, Ytrn) coefs = model.coefs_ yhat = model.predict(X) yhatval = model.predict(Xval) loss = np.square(Yval - yhatval).mean() hiddens = coefs[0].T final_mlp = coefs[1].flatten() coefs = list(zip([dict(zip(X.columns, h)) for h in hiddens], [['output mult:', m] for m in final_mlp.flatten()], [['intercept:', i] for i in model.intercepts_[0]])) print('loss:', loss) if print_coefs: for idx, c in enumerate(coefs): f1, o, i = c print('feature', idx, '=', f1['brand'].round(2), '* brand +', f1['d2c'].round(2), '* d2c', '+', i[1].round(2)) output = 'yhat = ' for fidx, v in enumerate(final_mlp): output = output + str(v.round(2)) + ' * feat ' + str(fidx) + ' + ' output = output + str(model.intercepts_[1][0].round(2)) print(output) return model, yhat, coefs, loss
我们用原始销售和支出数据使用我们的函数,没有增强特征。
dataset = pd.DataFrame({ 'brand': brand_spend, 'd2c': d2c_spend, 'sales': sales }).round(5) model, yhat, coefs, loss = model_sales_MLP(dataset, [6]) graph_real_and_predicted(dataset, yhat, 'neural_network')
最后,我们再次调用该函数,但使用深度神经网络。
model, yhat, coefs, loss = model_sales_MLP(dataset, [100, 100], max_iter = 1000, print_coefs=False) graph_real_and_predicted(dataset, yhat, fname = None)