基于Scipy Optimize的广告预算周期优化技术求助
广告预算优化实现方案
问题概述
正在优化一项涉及2款产品的广告预算计划,每款产品对应不同时长的广告,优化器需达成两个核心目标:
- 确定广告投放的周次
- 在给定产品预算的前提下,为每个广告分配预算
需要在10周的时间窗口内,以最优方式分配广告投放与预算,最大化目标函数(实现广告投放与预算分配的最优配置)。
核心输入信息
Total Advertising Budget: £150K Product-A Budget: £50K Product-B Budget: £100K Adverts Duration Table: |‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾| | Advert_No | Duration | Products | |___________|__________|__________| | Advert_1 | 2 Weeks | Product_A| | Advert_2 | 4 Weeks | Product_A| | Advert_3 | 5 Weeks | Product_B| | Advert_4 | 3 Weeks | Product_A| | Advert_5 | 2 Weeks | Product_B| | Advert_6 | 1 Weeks | Product_A| | Advert_7 | 3 Weeks | Product_B| ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
期望输出示例
|‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾| | Weeks | Product A | Product B | |_______|_______________|_______________| |Week 1 | Advert_4 | Advert_5 | |Week 2 | (£15k) | (£50k) | |Week 3 |_______________|‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾| |Week 4 | Advert_6 (£5k)| Advert_7 | |Week 5 |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾| (£15k) | |Week 6 | Advert_2 |‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾| |Week 7 | (£10k) | Advert_3 | |Week 8 |_______________| (£35k) | |Week 9 | Advert_1 | | |Week 10| (£20k) | | ‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
现有代码实现(待修正)
# Import Libraries import pandas as pd import numpy as np import scipy.optimize as so import random # Define Objective function (Maximization) def obj_func(matrix): def prod_a_func(x): # Advert budgets for Prod_a is concave Exponential Function return (1 - np.exp(-x / 70000)) * 0.2 def prod_b_func(x): # Advert budgets for Prod_a is concave Exponential Function return (1 - np.exp(-x / 200000)) * 0.6 prod_a = prod_a_func(matrix.reshape((-1, 2))[:,0]) prod_b = prod_b_func(matrix.reshape((-1, 2))[:,1]) output_matrix = np.column_stack((prod_a, prod_b)) return np.sum(output_matrix) # Create optimizer function def optimizer_result(tot_budget, col_budget_list, bucket_size_list): # Create constraint 1) - total matrix sum range constraints_list = [{'type': 'eq', 'fun': lambda x: np.sum(x) - tot_budget}, {'type': 'eq', 'fun': lambda x: (sum(x[i] for i in range(0, 10, 5)) - col_budget_list[0])}, {'type': 'eq', 'fun': lambda x: (sum(x[i] for i in range(1, 10, 5)) - col_budget_list[1])}, {'type': 'eq', 'fun': lambda x, advert_len_list[0]: [item for item in x for i in range(advert_len_list[0])]}, {'type': 'eq', 'fun': lambda x, advert_len_list[1]: [item for item in x for i in range(advert_len_list[1])]}] # Create an inital matrix start_matrix = [random.randint(0, 3) for i in range(0, 10)] # Run optimizer optimizer_solution = so.minimize(obj_func, start_matrix, method='SLSQP', bounds=[(0, tot_budget)] * 10, tol=0.01, options={'disp': True, 'maxiter': 100}, constraints=constraints_list) return optimizer_solution # Initalise constraints tot_budget = 150000 col_budget_list = [100000, 50000] advert_len_list = [[2,4,3,1], [5,2,3]] # Run Optimizer y = optimizer_result(tot_budget, col_budget_list, advert_len_list) advert_plan = pd.DataFrame(y['x'].reshape(-1,2),columns=["Product-A", "Product-B"])
补充数学约束
需要优化一个10×2的矩阵以实现ROI最大化,约束条件如下:
- 矩阵所有元素之和等于总预算
- 每一列的元素之和等于对应产品的预算(例如第0列之和等于Product-A的预算)
- 可设置单周预算(例如元素[1,3]可设为£10k)
- 广告投放的具体时间不影响结果
- 仅需确保广告时长适配10周窗口,以实现ROI最大化
代码修正方案
1. 目标函数调整
scipy.optimize.minimize默认求最小值,需将最大化目标转为最小化问题,返回目标函数的负值:
def obj_func(matrix): def prod_a_func(x): # Product-A的凹性收益函数 return (1 - np.exp(-x / 70000)) * 0.2 def prod_b_func(x): # Product-B的凹性收益函数 return (1 - np.exp(-x / 200000)) * 0.6 # 重塑为10周×2产品的矩阵 weekly_budgets = matrix.reshape((10, 2)) prod_a_returns = prod_a_func(weekly_budgets[:, 0]) prod_b_returns = prod_b_func(weekly_budgets[:, 1]) # 返回负值,将最大化问题转为最小化问题 return -np.sum(prod_a_returns + prod_b_returns)
2. 约束条件修正
原约束存在语法错误和逻辑偏差,重新梳理后修正:
def optimizer_result(tot_budget, prod_a_budget, prod_b_budget, prod_a_ad_durations, prod_b_ad_durations): n_weeks = 10 n_products = 2 total_vars = n_weeks * n_products # 约束1:总预算等于给定值 def total_budget_constraint(x): return np.sum(x) - tot_budget # 约束2:Product-A总预算达标 def prod_a_total_constraint(x): return np.sum(x.reshape(n_weeks, n_products)[:, 0]) - prod_a_budget # 约束3:Product-B总预算达标 def prod_b_total_constraint(x): return np.sum(x.reshape(n_weeks, n_products)[:, 1]) - prod_b_budget # 约束4:Product-A广告总时长适配10周 def prod_a_duration_constraint(x): return sum(prod_a_ad_durations) - n_weeks # 约束5:Product-B广告总时长适配10周 def prod_b_duration_constraint(x): return sum(prod_b_ad_durations) - n_weeks constraints_list = [ {'type': 'eq', 'fun': total_budget_constraint}, {'type': 'eq', 'fun': prod_a_total_constraint}, {'type': 'eq', 'fun': prod_b_total_constraint}, {'type': 'eq', 'fun': prod_a_duration_constraint}, {'type': 'eq', 'fun': prod_b_duration_constraint} ] # 初始化矩阵:按产品预算均匀分配到每周,避免随机值导致优化收敛慢 start_matrix = np.zeros(total_vars) start_matrix[::2] = prod_a_budget / n_weeks # Product-A每周初始预算 start_matrix[1::2] = prod_b_budget / n_weeks # Product-B每周初始预算 # 变量边界:单周预算不能为负,上限不超过对应产品总预算 bounds = [(0, prod_a_budget)] * n_weeks + [(0, prod_b_budget)] * n_weeks # 运行优化器,增加迭代次数提升收敛效果 optimizer_solution = so.minimize( obj_func, start_matrix, method='SLSQP', bounds=bounds, tol=0.01, options={'disp': True, 'maxiter': 500}, constraints=constraints_list ) return optimizer_solution
3. 参数初始化修正
原参数中产品预算顺序错误,修正后调用:
# 初始化参数 tot_budget = 150000 prod_a_budget = 50000 prod_b_budget = 100000 prod_a_ad_durations = [2,4,3,1] prod_b_ad_durations = [5,2,3] # 运行优化器 y = optimizer_result(tot_budget, prod_a_budget, prod_b_budget, prod_a_ad_durations, prod_b_ad_durations) advert_plan = pd.DataFrame(y['x'].reshape(10,2), columns=["Product-A", "Product-B"])
4. 广告周次分配
优化完成后,可根据预算分配结果,将连续周分配给对应时长的广告:
def assign_adverts(weekly_budget, ad_durations, ad_names): ad_assignments = [] remaining_weeks = weekly_budget.copy() # 按时长从长到短分配,避免短广告占用长广告的连续区间 sorted_pairs = sorted(zip(ad_durations, ad_names), key=lambda x: -x[0]) for dur, name in sorted_pairs: max_sum = -1 start_idx = 0 # 寻找连续dur周中预算总和最高的区间 for i in range(len(remaining_weeks) - dur + 1): current_sum = sum(remaining_weeks[i:i+dur]) if current_sum > max_sum: max_sum = current_sum start_idx = i # 记录广告分配的周次和总预算 ad_assignments.append({ "advert_name": name, "start_week": start_idx + 1, "end_week": start_idx + dur, "total_budget": round(max_sum, 2) }) # 标记已分配的周,避免重复分配 for i in range(start_idx, start_idx+dur): remaining_weeks[i] = 0 return ad_assignments # 为Product-A分配广告 prod_a_ad_names = ["Advert_1", "Advert_2", "Advert_4", "Advert_6"] prod_a_assignments = assign_adverts(advert_plan["Product-A"].tolist(), prod_a_ad_durations, prod_a_ad_names) # 为Product-B分配广告 prod_b_ad_names = ["Advert_3", "Advert_5", "Advert_7"] prod_b_assignments = assign_adverts(advert_plan["Product-B"].tolist(), prod_b_ad_durations, prod_b_ad_names)
内容的提问来源于stack exchange,提问作者star_it8293
相关产品推荐
相关产品推荐

