Cómo configurar el modelo

Puedes personalizar y configurar la especificación del modelo base de Meridian según tus necesidades específicas, como personalizar las distribuciones a priori del ROI, realizar ajustes en función de la tendencia y la estacionalidad, establecer la duración máxima de transferencia, usar datos de alcance y frecuencia, y mucho más. Para obtener más información sobre el modelo base y las opciones de Meridian, consulta la sección El modelo de Meridian.

Especificaciones del modelo predeterminado

Puedes usar la siguiente especificación del modelo predeterminado para comenzar a crear tu propio modelo:

model_spec = spec.ModelSpec(
    prior=prior_distribution.PriorDistribution(),
    media_effects_dist='log_normal',
    hill_before_adstock=False,
    max_lag=8,
    unique_sigma_for_each_geo=False,
    media_prior_type='roi',
    roi_calibration_period=None,
    rf_prior_type='roi',
    rf_roi_calibration_period=None,
    organic_media_prior_type='contribution',
    organic_rf_prior_type='contribution',
    non_media_treatments_prior_type='contribution',
    knots=None,
    baseline_geo=None,
    holdout_id=None,
    control_population_scaling_id=None,
    adstock_decay_spec='geometric',
    enable_aks=False,
)

Cómo establecer las distribuciones a priori

Puedes personalizar las distribuciones a priori de la especificación del modelo predeterminado. Cada parámetro obtiene su propia distribución a priori independiente, la cual se puede establecer con el argumento prior en ModelSpec de Meridian. Para obtener información sobre las distribuciones a priori y las excepciones, consulta Distribuciones a priori predeterminadas.

En el siguiente ejemplo, se personalizan las distribuciones a priori del ROI para cada canal de medios. En este caso en particular, las distribuciones a priori del ROI difieren para cada canal de medios.

  • Canal 1: LogNormal(0.2, 0.7)
  • Canal 2: LogNormal(0.3, 0.9)
  • Canal 3: LogNormal(0.4, 0.6)
  • Canal 4: LogNormal(0.3, 0.7)
  • Canal 5: LogNormal(0.3, 0.6)
  • Canal 6: LogNormal(0.4, 0.5)
my_input_data = input_data.InputData( ... )
build_media_channel_args = my_input_data.get_paid_media_channels_argument_builder()

# Assuming Channel1,...,Channel6 are all media channels.
roi_m = build_media_channel_args(
  Channel1=(0.2, 0.7),
  Channel2=(0.3, 0.9),
  Channel3=(0.4, 0.6),
  Channel4=(0.3, 0.7),
  Channel5=(0.3, 0.6),
  Channel6=(0.4, 0.5),
) # This creates a list of channel-ordered (mu, sigma) tuples.
roi_m_mu, roi_m_sigma = zip(*roi_m)

prior = prior_distribution.PriorDistribution(
    roi_m=tfp.distributions.LogNormal(
        roi_m_mu, roi_m_sigma, name=constants.ROI_M
    )
)
model_spec = spec.ModelSpec(prior=prior)

Aquí prior en ModelSpec es un objeto PriorDistribution que especifica la distribución a priori de cada conjunto de parámetros del modelo. Cada parámetro obtiene su propia distribución a priori independiente, la cual se puede establecer con el constructor prior_distribution.PriorDistribution().

Los parámetros del modelo con el subíndice m (por ejemplo, roi_m) pueden tener una dimensionalidad igual a la cantidad de canales de medios o ser unidimensionales. Cuando la dimensionalidad es igual a la cantidad de canales de medios, el orden de los valores de los parámetros en la distribución a priori personalizada coincide con el de data.media_channel. Esto representa una distribución a priori personalizada para cada uno de los respectivos canales de medios. Si no puedes determinar una distribución a priori personalizada para algunos de los canales de medios, considera usar la configuración predeterminada tfd.LogNormal(0.2, 0.9) de forma manual. Cuando se pasa una distribución a priori unidimensional, se usa una única dimensión para todos los canales de medios.

La lógica para establecer las distribuciones a priori para los parámetros del modelo con el subíndice c (por ejemplo, gamma_c) es la misma que para el subíndice m. Los parámetros con el subíndice c pueden tener una dimensionalidad igual a la cantidad de variables de control o ser unidimensionales. Cuando la dimensionalidad es igual a la cantidad de variables de control, el orden de los valores de los parámetros en la distribución a priori personalizada coincide con el de data.control_variable. Esto representa una distribución a priori personalizada para cada una de las respectivas variables de control.

En el siguiente ejemplo, se utiliza un único número para establecer la misma distribución a priori del ROI en cada canal. En este caso en particular, las distribuciones a priori del ROI de los dos canales de medios son idénticas y ambas se representan como LogNormal(0.2, 0.9).

import tensorflow_probability as tfp
from meridian.model import prior_distribution
from meridian.model import spec

roi_mu = 0.2
roi_sigma = 0.9
prior = prior_distribution.PriorDistribution(
    roi_m=tfp.distributions.LogNormal(roi_mu, roi_sigma, name=constants.ROI_M)
)
model_spec = spec.ModelSpec(prior=prior)

Es importante destacar que Meridian ofrece dos parámetros distintos, uno de ROI (roi_rf) y otro beta (beta_rf), para los canales con datos de alcance y frecuencia. En consecuencia, se deben hacer algunas modificaciones en los fragmentos de código antes mencionados cuando ciertos canales tengan datos de alcance y frecuencia. En este ejemplo, los canales 4 y 5 tienen datos de alcance y frecuencia.

  • Si deseas personalizar las distribuciones a priori del ROI para cada canal de medios, consulta esta información:

    # ROI prior for channels without R&F data
    build_media_channel_args = my_input_data.get_paid_media_channels_argument_builder()
    roi_m = build_media_channel_args(
      Channel1=(0.2, 0.7),
      Channel2=(0.3, 0.9),
      Channel3=(0.4, 0.6),
      Channel4=(0.3, 0.7),
    )
    roi_m_mu, roi_m_sigma = zip(*roi_m)
    
    # ROI prior for channels with R&F data
    build_rf_channel_args = my_input_data.get_paid_rf_channels_argument_builder()
    roi_rf = build_rf_channel_args(
      Channel5=(0.3, 0.6),
      Channel6=(0.4, 0.5),
    ]
    roi_rf_mu, roi_rf_sigma = zip(*roi_rf)
    
    prior = prior_distribution.PriorDistribution(
        roi_m=tfp.distributions.LogNormal(
            roi_m_mu, roi_m_sigma, name=constants.ROI_M
        ),
        roi_rf=tfp.distributions.LogNormal(
            roi_rf_mu, roi_rf_sigma, name=constants.ROI_RF
        ),
    )
    model_spec = spec.ModelSpec(prior=prior)
    

Ten en cuenta que el orden de los valores de los parámetros en roi_rf_mu y roi_rf_sigma debe coincidir con el de data.rf_channel.

  • Si deseas establecer las mismas distribuciones a priori del ROI para todos los canales de medios, consulta esta información:

    roi_mu = 0.2
    roi_sigma = 0.9
    prior = prior_distribution.PriorDistribution(
        roi_m=tfp.distributions.LogNormal(
            roi_mu, roi_sigma, name=constants.ROI_M),
        roi_rf=tfp.distributions.LogNormal(
            roi_mu, roi_sigma, name=constants.ROI_RF
        ),
    )
    model_spec = spec.ModelSpec(prior=prior)
    

Cómo usar una división de datos de entrenamiento y prueba (opcional)

Recomendamos utilizar una división de datos de entrenamiento y prueba para evitar el sobreajuste y garantizar la correcta generalización del modelo cuando haya datos nuevos. Esto se puede hacer con holdout_id. Este paso es opcional.

En el siguiente ejemplo, se muestra un argumento holdout_id que establece de forma aleatoria el 20% de los datos como el grupo de prueba:

np.random.seed(1)
test_pct = 0.2  # 20% of data are held out
n_geos = len(data.geo)
n_times = len(data.time)
holdout_id = np.full([n_geos, n_times], False)
for i in range(n_geos):
  holdout_id[
    i,
    np.random.choice(
      n_times,
      int(np.round(test_pct * n_times)),
    )
  ] = True
model_spec = spec.ModelSpec(holdout_id=holdout_id)

Aquí holdout_id es un tensor booleano opcional de las dimensiones (n_geos × n_times) o (n_times) que indica qué observaciones se excluyen de la muestra de entrenamiento. Solo la variable de respuesta se excluye de la muestra de entrenamiento. Las variables de medios permanecen incluidas, ya que pueden afectar el Adstock de las semanas posteriores. La configuración predeterminada es None, lo que significa que no se excluye ninguna ubicación geográfica ni período.

Cómo ajustar el intercepto variable en el tiempo para modelar los efectos temporales (opcional)

Meridian usa un enfoque de intercepto variable en el tiempo para modelar los efectos temporales. Los efectos pueden ajustarse modificando el valor de knots. Para obtener más información, consulta Cómo funciona el argumento knots.

knots es un número entero o una lista de números enteros opcional que indica los nudos utilizados para estimar los efectos temporales. Cuando knots es una lista de números enteros, las ubicaciones de los nudos se proporcionan en esa lista, donde cero corresponde a un nudo en el primer período, uno corresponde a un nudo en el segundo período y así sucesivamente, hasta (n_times - 1), que corresponde a un nudo en el último período.

Cuando knots es un número entero, hay esa cantidad de nudos con ubicaciones equidistantes entre los períodos, incluidos los nudos en cero y (n_times - 1). Cuando knots es 1, hay un único coeficiente de regresión común que se usa para todos los períodos.

En el caso de los modelos geográficos, si knots se establece en None, la cantidad de nudos utilizados es igual a la cantidad de períodos. Esto equivale a que cada período tenga su propio coeficiente de regresión. En el caso de los modelos nacionales, si knots se establece en None, la cantidad de nudos utilizados es 1. De forma predeterminada, su valor se establece en None.

Consulta Cómo elegir la cantidad de nudos para los efectos del tiempo en el modelo para obtener información al respecto.

Selección automática de nudos

La función de selección automática de nudos (AKS) de Meridian selecciona automáticamente la cantidad y la ubicación de los nudos para explicar la tendencia y la estacionalidad en la variable de resultado. Consulta Selección automática de nudos en Meridian para obtener más información.

Para habilitar AKS, establece el parámetro enable_aks en True en tu ModelSpec.

from meridian.model import model
from meridian.model import spec

model_spec = spec.ModelSpec(enable_aks=True)
mmm = model.Meridian(model_spec=model_spec)

Especificación manual de nudos

Para colocar nudos de forma manual y modelar los efectos basados en el tiempo, usa el parámetro knots en ModelSpec. El parámetro knots acepta un número entero (para una cantidad específica de nudos equidistantes) o una lista de números enteros (para ubicaciones de nudos específicas).

  • Si knots se establece en 1, se usa un solo parámetro para todos los períodos, lo que quita de manera efectiva cualquier efecto automático basado en el tiempo. En este caso, debes incluir variables de control, como pseudovariables de eventos, para reflejar esos efectos temporales.

    model_spec = spec.ModelSpec(knots=1)
    
  • Puedes establecer knots en un número relativamente alto:

    knots = round(0.8 * n_times)
    model_spec = spec.ModelSpec(knots=knots)
    
  • Puedes establecer knots cada 4 puntos temporales:

    knots = np.arange(0, n_times, 4).tolist()
    model_spec = spec.ModelSpec(knots=knots)
    
  • Puedes establecer knots para que haya un nudo en noviembre y diciembre, pero que haya relativamente pocos en otros momentos. Para que el ejemplo no sea demasiado complejo, supongamos que solo hay 12 datos y que los datos son mensuales (esta suposición no es realista ni se recomienda). Este ejemplo se resume en la siguiente tabla:

    Establecer nudos por mes

  • Para establecer knots en los índices 0, 3, 6, 10 y 11, haz lo siguiente:

    knots = [0, 3, 6, 10, 11]
    model_spec = spec.ModelSpec(knots=knots)
    
  • Para agregar nudos personalizados a los seleccionados por AKS, primero recupera los nudos elegidos por el algoritmo y, luego, agrega tus nudos personalizados en esa lista:

    # Enable AKS in Meridian
    model_spec = spec.ModelSpec(enable_aks=True)
    mmm = model.Meridian(model_spec=model_spec)
    # Retrieve the list of knots that AKS selects
    selected_knots = mmm.knot_info()
    # Add a new knot at time point 52
    # Create a new list and use it to define a new ModelSpec
    modified_knots = selected_knots + [52]
    model_spec = spec.ModelSpec(knots=modified_knots)
    

Cómo ajustar la calibración del ROI (opcional)

Meridian presenta un método de calibración del ROI que permite reparametrizar el ROI como parámetro del modelo. Para obtener más información, consulta Distribuciones a priori del ROI para la calibración.

De forma predeterminada, se aplica la misma distribución a priori no informativa del ROI a todos los canales de medios. Hay dos acciones que te permiten ajustar esta función:

  • Desactivar la calibración del ROI
  • Establecer el período de calibración del ROI

Cómo desactivar la calibración del ROI

Puedes desactivar la función de calibración del ROI utilizando media_prior_type='coefficient' y rf_prior_type='coefficient':

model_spec = spec.ModelSpec(
    media_prior_type='coefficient',
    rf_prior_type='coefficient',
)

El argumento media_prior_type indica si se debe usar una distribución a priori en roi_m, mroi_m o beta_m en PriorDistribution. Configuración predeterminada: 'roi' (recomendada).

Cómo establecer el período de calibración del ROI

Aunque los coeficientes de regresión de los efectos de los medios no tienen efectos que varíen con el tiempo, hay un argumento de período de calibración para establecer la distribución a priori del ROI (o del mROI). Esto se debe a que el ROI (o el mROI) en un momento determinado depende de factores adicionales que pueden variar con el tiempo:

  • Las curvas de Hill modelan la disminución no lineal de los retornos asociados con la ejecución de los medios. Por lo tanto, el grado de ejecución de los medios en un momento determinado puede afectar el ROI.
  • La asignación de medios en las diferentes ubicaciones geográficas y su efectividad pueden variar.
  • El costo de la ejecución de medios puede variar.

Puedes calibrar el MMM con un subconjunto de datos cuando los resultados experimentales no reflejen el retorno de la inversión publicitaria (ROAS) modelado que el MMM intenta medir. Por ejemplo, el período del experimento no coincide con el período de los datos del MMM. Para obtener más información, consulta Calibración del modelo de combinación de medios con distribuciones a priori bayesianas y Distribuciones a priori y calibración del ROI.

En el siguiente ejemplo, se muestra cómo especificar el período de calibración del ROI del '2021-11-01' al '2021-12-20' para el canal 1. Los canales de medios que no se especifiquen en roi_period utilizarán todos los períodos disponibles para la calibración del ROI.

roi_period = {
  'Channel1': [
    '2021-11-01',
    '2021-11-08',
    '2021-11-15',
    '2021-11-22',
    '2021-11-29',
    '2021-12-06',
    '2021-12-13',
    '2021-12-20',
  ],
}

roi_calibration_period = np.zeros((len(data.time), len(data.media_channel)))
for i in roi_period.items():
  roi_calibration_period[
      np.isin(data.time.values, i[1]), data.media_channel.values == i[0]
  ] = 1

roi_calibration_period[
    :, ~np.isin(data.media_channel.values, list(roi_period.keys()))
] = 1

model_spec = spec.ModelSpec(roi_calibration_period=roi_calibration_period)

Aquí roi_calibration_period es un array booleano opcional con la forma (n_media_times, n_media_channels) que indica el subconjunto de time para la calibración del ROI de los medios. Si se establece en None, se utilizarán todos los períodos para la calibración del ROI de los medios. Configuración predeterminada: None.

Es importante destacar que Meridian ofrece un parámetro diferente, rf_roi_calibration_period, para los canales con datos de alcance y frecuencia. En el siguiente ejemplo, se muestra cómo especificar el período de calibración del ROI del '2021-11-01' al '2021-12-20' para el canal 5, que usa datos de entrada de alcance y frecuencia.

roi_period = {
  'Channel5': [
    '2021-11-01',
    '2021-11-08',
    '2021-11-15',
    '2021-11-22',
    '2021-11-29',
    '2021-12-06',
    '2021-12-13',
    '2021-12-20',
  ],
}

rf_roi_calibration_period = np.zeros(len(data.time), len(data.rf_channel))
for i in roi_period.items():
  rf_roi_calibration_period[
      np.isin(data.time.values, i[1]), data.rf_channel.values == i[0]
  ] = 1

rf_roi_calibration_period[
    :, ~np.isin(data.rf_channel.values, list(roi_period.keys()))
] = 1

model_spec = spec.ModelSpec(rf_roi_calibration_period=rf_roi_calibration_period)

Aquí rf_roi_calibration_period es un array booleano opcional con la forma (n_media_times, n_rf_channels). Si se establece en None, se utilizarán todos los períodos y ubicaciones geográficas para la calibración del ROI de los medios. La configuración predeterminada es None.

Cómo establecer atributos adicionales (opcional)

Puedes cambiar los atributos restantes de la especificación del modelo predeterminado según sea necesario. En esta sección, se describen los atributos restantes y ejemplos de cómo cambiar los valores.

baseline_geo

Es un número entero o una cadena opcionales que indican la ubicación geográfica de referencia. Esta ubicación se usa como referencia en la pseudocodificación de ubicaciones geográficas. Las ubicaciones geográficas que no son de referencia tienen una variable de indicador tau_g correspondiente, lo que significa que tienen una varianza a priori más alta que las de referencia. Cuando se establece en None, se utiliza la ubicación geográfica con la mayor población como referencia. La configuración predeterminada es None.

En el siguiente ejemplo, se muestra la ubicación geográfica de referencia establecida en 'Geo10':

model_spec = spec.ModelSpec(baseline_geo='Geo10')

hill_before_adstock

Es un valor booleano que indica si se debe aplicar la función de Hill antes que la función de Adstock, lo que se opone al orden predeterminado (Adstock antes que Hill). La configuración predeterminada es False.

En los siguientes ejemplos, se muestra que la función de Hill se aplicará primero, ya que el valor se establece en True:

model_spec = spec.ModelSpec(hill_before_adstock=True)

max_lag

Es un número entero que indica la cantidad máxima de períodos de retraso (≥ 0) que se deben incluir en el cálculo de Adstock. Para obtener más detalles sobre cómo establecer max_lag, consulta Cómo establecer el parámetro max_lag. La configuración predeterminada es 8.

En el siguiente ejemplo, se cambia el valor a 4:

model_spec = spec.ModelSpec(max_lag=4)

media_effects_dist

Es una cadena que permite especificar la distribución de los efectos aleatorios de los medios en las ubicaciones geográficas. Los valores permitidos son 'normal' o 'log_normal'. La configuración predeterminada es 'log_normal'.

Aquí ocurre lo siguiente:

  • media_effects_dist='log_normal' es \(\beta m,g\ {_\sim^{iid}} Lognormal(\beta m, \eta ^2m)\).

  • media_effects_dist='normal' es \(\beta m,g\ {_\sim^{iid}} Normal (\beta m, \eta ^2m)\).

En el siguiente ejemplo, se muestra cómo cambiar el valor a 'normal':

model_spec = spec.ModelSpec(media_effects_dist='normal')

control_population_scaling_id

Es un tensor booleano opcional de dimensión (n_controls) que indica las variables de control para las que se ajustará el valor de control según la población. La configuración predeterminada es None.

En el siguiente ejemplo, se especifica que la variable de control en el índice 1 se debe ajustar según la población:

control_population_scaling_id = np.full([n_controls], False)
control_population_scaling_id[1] = True
model_spec = spec.ModelSpec(
  control_population_scaling_id=control_population_scaling_id
)

unique_sigma_for_each_geo

Es un valor booleano que indica si se debe usar una varianza residual única para cada ubicación geográfica. Si es False, se usa una sola varianza residual para todas las ubicaciones geográficas. La configuración predeterminada es False.

En el siguiente ejemplo, se establece el modelo para que use una varianza residual única para cada ubicación geográfica:

model_spec = spec.ModelSpec(unique_sigma_for_each_geo=True)

adstock_decay_spec

Es una cadena o una asignación que indica la función de decaimiento de Adstock para cada canal de medios, alcance y frecuencia, medios orgánicos, y alcance y frecuencia orgánicos. Si adstock_decay_spec es una cadena, su valor debe ser 'geometric' o 'binomial', lo que indica qué función de decaimiento se debe usar para todos los canales que utilizan la transformación de Adstock. Si adstock_decay_spec es una asignación, las claves deben ser nombres de canales y deben ser 'geometric' o 'binomial', y cada par clave-valor debe denotar la función de decaimiento de Adstock que se usará para ese canal. Los canales que no se especifiquen en la asignación usarán 'geometric' de forma predeterminada. Valor predeterminado: 'geometric'.

En el siguiente ejemplo, se establece el modelo para utilizar la disminución binomial en todos los canales:

from meridian.model import spec

model_spec = spec.ModelSpec(
  adstock_decay_spec='binomial'
)

Mientras que, para establecer el modelo que se usará, se deben usar los modelos binomial, geométrico y binomial para tres canales llamados 'Channel0', 'Channel1' y 'Channel2', respectivamente:

from meridian.model import spec

model_spec = spec.ModelSpec(
  adstock_decay_spec=dict(
    Channel0='binomial',
    Channel1='geometric',
    Channel2='binomial',
  )
)