You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 15:24:55