Introducción
de la toma de decisiones basada en datos. La mayoría de las organizaciones no sólo mantienen bases de datos masivas de información, sino que también cuentan con innumerables equipos que dependen de estos datos para informar su toma de decisiones. Desde el tráfico de flujo de clics hasta los dispositivos portátiles, la telemetría y mucho más, la velocidad y la escala de la toma de decisiones basada en datos están aumentando exponencialmente, impulsando la popularidad de la integración de marcos de aprendizaje automático y IA.
Hablando de marcos de toma de decisiones basados en datos, uno de los enfoques más confiables y probados son las pruebas A/B. Las pruebas A/B son especialmente populares entre sitios web, productos digitales y medios similares donde los comentarios de los clientes en forma de clics, pedidos, etc., se reciben casi instantáneamente y a escala. Lo que hace que las pruebas A/B sean un marco de decisión tan poderoso es la capacidad de controlar innumerables variables para que una parte interesada pueda ver el efecto que tiene el elemento que están introduciendo en la prueba en un indicador clave de desempeño (KPI).
Como todo, las pruebas A/B tienen desventajas, particularmente el tiempo que pueden llevar. Tras la conclusión de una prueba, alguien debe comunicar los resultados y las partes interesadas deben utilizar los canales adecuados para tomar una decisión e implementarla. Todo ese tiempo perdido puede traducirse en un costo de oportunidad, suponiendo que la experiencia de la prueba haya demostrado un impacto. ¿Qué pasaría si existiera un marco o un algoritmo que pudiera automatizar sistemáticamente este proceso? Aquí es donde entra en juego Thompson Sampling.
El problema de los bandidos multiarmados
Imagina que vas al casino por primera vez y, frente a ti, hay tres máquinas tragamonedas: la máquina A, la máquina B y la máquina C. No tienes idea de qué máquina tiene el mayor pago; sin embargo, se te ocurre una idea inteligente. Para los primeros tiros, suponiendo que no te quedes sin suerte, tiras de los brazos de la máquina tragamonedas al azar. Después de cada extracción, registra el resultado. Después de algunas iteraciones, observa sus resultados y observa la tasa de ganancias de cada máquina:
Máquina A: 40% Máquina B: 30% Máquina C: 50%
En este punto, decide utilizar la Máquina C a una tasa ligeramente mayor que las otras dos, ya que cree que hay más evidencia de que la Máquina C tiene la tasa de ganancia más alta, pero desea recopilar más datos para estar seguro. Después de las siguientes iteraciones, echa un vistazo a los nuevos resultados:
Máquina A: 45% Máquina B: 25% Máquina C: 60%
Ahora tiene mucha más confianza en que la Máquina C tiene la tasa de victorias más alta. Este ejemplo hipotético es lo que le dio su nombre al problema de los bandidos multiarmados y es un ejemplo clásico de cómo se utiliza el muestreo Thompson.
Este algoritmo bayesiano está diseñado para elegir entre múltiples opciones con distribuciones de recompensa desconocidas y maximizar la recompensa esperada. Lo logra mediante el equilibrio entre exploración y explotación. Dado que se desconocen las distribuciones de recompensas, el algoritmo elige opciones al azar, recopila datos sobre los resultados y, con el tiempo, elige progresivamente opciones a un ritmo más alto que generan una recompensa promedio más alta.
En este artículo, le explicaré cómo puede crear su propio objeto del algoritmo de muestreo Thompson en Python y aplicarlo a un ejemplo hipotético pero de la vida real.
Titulares de correo electrónico: optimización de la tasa de apertura
en Unsplash. De uso gratuito bajo la licencia Unsplash
En este ejemplo, asuma el papel de alguien en una organización de marketing encargado de las campañas de correo electrónico. En el pasado, el equipo probó qué titulares conducían a tasas de apertura de correo electrónico más altas utilizando un marco de prueba A/B. Aún así, esta vez recomienda implementar un enfoque de bandido con múltiples brazos para comenzar a obtener valor más rápido.
Para demostrar la efectividad de un enfoque de Thompson Sampling (también conocido como bandido), construiré una simulación de Python que lo compare con un enfoque aleatorio. Empecemos.
Paso 1: simulación de correo electrónico base
Este será el objetivo principal de este proyecto; Servirá como plantilla base para las simulaciones aleatorias y de bandidos. La función de inicialización almacena información básica necesaria para ejecutar la simulación del correo electrónico, específicamente, los titulares de cada correo electrónico y las tasas de apertura reales. Un elemento que quiero destacar son las verdaderas tasas de apertura. Serán "desconocidos" para la simulación real y serán tratados como probabilidades cuando se envíe un correo electrónico. También se crea un objeto generador de números aleatorios para permitir replicar una simulación, lo que puede ser útil. Finalmente, tenemos una función incorporada, reset_results(), que discutiré a continuación.
importar numpy como np importar pandas como pd clase BaseEmailSimulation: """ Clase base para simulaciones de titulares de correo electrónico. Responsabilidades compartidas: – almacenar titulares y sus verdaderas probabilidades de apertura – simular un resultado binario de apertura de correo electrónico – restablecer el estado de simulación – crear una tabla de resumen a partir de la última ejecución """ def __init__(self, titulares, true_probabilities, random_state=None): self.headlines = list(headlines) self.true_probabilities = np.array(true_probabilities, dtype=float) if len(self.headlines) == 0: rise ValueError("Se debe proporcionar al menos un título.") if len(self.headlines) != len(self.true_probabilities): rise ValueError("los titulares y true_probabilities deben tener la misma longitud.") if np.any(self.true_probabilities < 0) o np.any(self.true_probabilities > 1): rise ValueError("Todas las probabilidades_verdaderas deben estar entre 0 y 1.") self.n_arms = len(self.headlines) self.rng = np.random.default_rng(random_state) # Información sobre el mejor brazo de verdad sobre el terreno para evaluación self.best_arm_index = int(np.argmax(self.true_probabilities)) self.best_headline = self.headlines[self.best_arm_index] self.best_true_probability = float(self.true_probabilities[self.best_arm_index]) # Resultados de la última simulación completa self.reset_results()
restablecer_resultados()
Para cada simulación, es útil tener muchos detalles, incluidos:
Qué título se seleccionó en cada paso, independientemente de si el correo electrónico enviado generó o no una tasa de apertura y apertura general.
Los atributos no están definidos explícitamente en esta función; se definirán más adelante. En cambio, esta función los restablece, permitiendo un historial nuevo para cada ejecución de simulación. Esto es especialmente importante para la subclase de bandidos, que les mostraré más adelante en el artículo.
def reset_results(self): """ Borra todos los resultados de la última simulación. Se llama automáticamente en la inicialización y al inicio de cada ejecución(). """ self.reward_history =[]self.selection_history =[]self.history = pd.DataFrame() self.summary_table = pd.DataFrame() self.total_opens = 0 self.cumulative_opens =[]
enviar_correo electrónico()
La siguiente función que debe destacarse es cómo se ejecutarán los envíos de correo electrónico. Dado un índice de brazo (índice de titular), la función muestra exactamente un valor de una distribución binomial con la tasa de probabilidad real para ese titular con exactamente una prueba independiente. Este es un enfoque práctico, ya que enviar un correo electrónico tiene exactamente dos resultados: se abre o se ignora. Abierto e ignorado estarán representados por 1 y 0, respectivamente, y la función binomial de numpy hará precisamente eso, siendo la probabilidad de que regrese "n" "1" igual a la probabilidad real del título de correo electrónico respectivo.
def send_email(self, arm_index): """ Simula el envío de un correo electrónico con el título seleccionado. Devuelve ——- int 1 si se abre, 0 en caso contrario. """ si arm_index < 0 o arm_index >= self.n_arms: rise IndexError("arm_index está fuera de límites.") true_p = self.true_probabilities[arm_index] recompensa = self.rng.binomial(n=1, p=true_p) devuelve int(recompensa)
_finalize_history() y build_summary_table()
Por último, estas dos funciones funcionan en conjunto tomando los resultados de una simulación y creando una tabla de resumen limpia que muestra métricas como la cantidad de veces que se seleccionó un titular, se abrió, la tasa de apertura real y la tasa de apertura realizada.
def _finalize_history(self, records): """ Convierte registros de nivel redondo en un DataFrame y completa los atributos de resultados compartidos. """ self.history = pd.DataFrame(records) if not self.history.empty: self.reward_history = self.history["reward"].tolist() self.selection_history = self.history["arm_index"].tolist() self.total_opens = int(self.history["recompensa"].sum()) self.cumulative_opens = self.history["recompensa"].cumsum().tolist() else: self.reward_history =[]self.selection_history =[]self.total_opens = 0 self.acumulative_opens =[]self.summary_table = self.build_summary_table() def build_summary_table(self): """ Crea una tabla de resumen a partir de la última simulación completada. Devuelve ——- pd.DataFrame Resumen por título. """ if self.history.empty: return pd.DataFrame(columns=[ "arm_index", "headline", "selections", "opens", "realized_open_rate", "true_open_rate" ]) resumen = ( self.history .groupby(["arm_index", "headline"], as_index=False) .agg( selecciones=("recompensa", "tamaño"), opens=("recompensa", "suma"), realizado_open_rate=("recompensa", "media"), true_open_rate=("true_open_rate", "primero")) .sort_values("arm_index") .reset_index(drop=True) ) resumen de retorno
Paso 2 – Subclase: Simulación aleatoria de correo electrónico
Para evaluar adecuadamente el impacto de un enfoque de bandidos armados múltiples para los titulares de correo electrónico, debemos compararlo con un punto de referencia, en este caso, un enfoque aleatorio, que también refleja cómo se ejecuta una prueba A/B.
seleccionar_título()
Este es el núcleo de la clase de simulación aleatoria de correo electrónico, select_headline() elige un número entero entre 0 y el número de titulares (o brazos) al azar.
def select_headline(self): """ Seleccione un título uniformemente al azar. """ return int(self.rng.integers(low=0, high=self.n_arms))
correr()
Así es como se ejecuta la simulación. Todo lo que se necesita es la cantidad de iteraciones del usuario final. Aprovecha la función select_headline() junto con la función send_email() de la clase principal. En cada ronda, se envía un correo electrónico y se registran los resultados.
def run(self, num_iterations): """ Ejecute una nueva simulación aleatoria desde cero. Parámetros ———- num_iterations: int Número de envíos de correo electrónico simulados. """ if num_iterations <= 0: rise ValueError("num_iterations debe ser mayor que 0.") self.reset_results() records =[]cumulative_opens = 0 para round_number in range(1, num_iterations + 1): arm_index = self.select_headline() recompensa = self.send_email(arm_index) cumulative_opens += registros de recompensa.append({ "round": round_number, "arm_index": arm_index, "headline": self.headlines[arm_index], "reward": recompensa, "true_open_rate": self.true_probabilities[arm_index], "cumulative_opens": acumulative_opens }) self._finalize_history(registros)
Muestreo Thompson y distribuciones Beta
Antes de sumergirnos en nuestra subclase de bandidos, es esencial cubrir con más detalle las matemáticas detrás de Thompson Sampling. Cubriré esto a través de nuestro ejemplo hipotético de correo electrónico en este artículo.
Consideremos primero lo que sabemos hasta ahora sobre nuestra situación actual. Hay un conjunto de titulares de correo electrónico y sabemos que cada uno tiene una tasa de apertura asociada. Necesitamos un marco para decidir qué título de correo electrónico enviar a un cliente. Antes de continuar, definamos algunas variables:
Titulares: 1: “Su oferta exclusiva de primavera ya está aquí”. 2: “Solo 48 horas: Ahorre 25 % 3: “No se pierda su descuento de miembro” 4: “Termina esta noche: última oportunidad de tener” 5: “Algo solo para usted” A_i = Titular (brazo) en el índice i t_i = Hora o número actual de la iteración (envío de correo electrónico) que se realizará r_i = La recompensa observada en el momento t_i, el resultado se abrirá o se ignorará
Todavía tenemos que enviar el primer correo electrónico. ¿Qué titular debemos seleccionar? Aquí es donde entra en juego la Distribución Beta. Una Distribución Beta es una distribución de probabilidad continua definida en el intervalo (0,1). Tiene dos variables clave que representan éxitos y fracasos, respectivamente, alfa y beta. En el momento t = 1, todos los titulares comienzan con alfa = 1 y beta = 1. Las aperturas de correo electrónico agregan 1 a alfa; de lo contrario, beta se incrementa en 1.
A primera vista, se podría pensar que el algoritmo supone una tasa de apertura real del 50% al principio. Este no es necesariamente el caso, y esta suposición descuidaría por completo el objetivo central del enfoque de Thompson Sampling: el equilibrio entre exploración y explotación. Las variables alfa y beta se utilizan para crear una distribución beta para cada título individual. Antes de la primera iteración, estas distribuciones se verán así:
Prometo que hay más que una simple línea horizontal. El eje x representa probabilidades de 0 a 1. El eje y representa la densidad de cada probabilidad, o el área bajo la curva. Usando esta distribución, tomamos una muestra de un valor aleatorio para cada correo electrónico y luego usamos el valor más alto como título del correo electrónico. En esta primera iteración, el marco de decisión es puramente aleatorio. ¿Por qué? Cada valor tiene la misma área bajo la curva. Pero ¿qué pasa después de algunas iteraciones más? Recuerde, cada recompensa se agrega a alfa o beta en la distribución beta respectiva. Veamos cómo se ve la distribución con alfa = 10 y beta = 10.
Ciertamente hay una diferencia, pero ¿qué significa eso en el contexto de nuestro problema? En primer lugar, si alfa y beta son iguales a 10, significa que seleccionamos ese titular 18 veces y observamos 9 éxitos (apertura de correo electrónico) y 9 fracasos (correo electrónico ignorado). Por lo tanto, la tasa de apertura realizada para este titular es 0,5 o 50%. Recuerde, siempre comenzamos con alfa y beta iguales a 1. Si tomamos una muestra aleatoria de un valor de esta distribución, ¿cuál cree que será? Lo más probable es que sea algo cercano a 0,5, pero no está garantizado. Veamos un ejemplo más y establezcamos alfa y beta iguales a 100.
Ahora hay una probabilidad mucho mayor de que un valor muestreado aleatoriamente esté alrededor de 0,5. Esta progresión demuestra cómo Thompson Sampling pasa sin problemas de la exploración a la explotación. Veamos cómo podemos construir un objeto que ejecute este marco.
Paso 3 – Subclase: Simulación de correo electrónico de Bandit
Echemos un vistazo a algunos atributos clave, comenzando con alpha_prior y beta_prior. Se establecen en 1 cada vez que se inicializa un objeto BanditSimulation(). “Anterior” es un término clave en este contexto. En cada iteración, nuestra decisión sobre qué titular enviar depende de una distribución de probabilidad, conocida como Posterior. A continuación, este objeto hereda algunos atributos seleccionados de la clase principal BaseEmailSimulation. Finalmente, se llama a una función personalizada llamada reset_bandit_state(). Analicemos esa función a continuación.
class BanditSimulation(BaseEmailSimulation): """ Simulación de título de correo electrónico de Thompson Sampling. Cada título se modela con un posterior Beta sobre su probabilidad de apertura desconocida. En cada iteración, se extrae una muestra de cada posterior y se selecciona el titular con la muestra más grande. """ def __init__( self, titulares, probabilidades_verdaderas, alfa_prior=1.0, beta_prior=1.0, estado_aleatorio=None ): super().__init__( titulares=titulares, probabilidades_verdaderas=probabilidades_verdaderas, estado_aleatorio=estado_aleatorio ) si alpha_prior <= 0 o beta_prior <= 0: aumente ValueError("alpha_prior y beta_prior deben ser positivos.") self.alpha_prior = float(alpha_prior) self.beta_prior = float(beta_prior) self.reset_bandit_state()
reset_bandit_state()
Los objetos que he creado para este artículo están pensados para ejecutarse en una simulación; por lo tanto, debemos incluir mecanismos de seguridad para evitar la fuga de datos entre simulaciones. La función reset_bandit_state() logra esto restableciendo la parte posterior de cada título cada vez que se ejecuta o cuando se inicia una nueva clase Bandit. De lo contrario, corremos el riesgo de ejecutar una simulación como si los datos ya hubieran sido recopilados, lo que frustra todo el propósito del enfoque de muestreo Thompson.
def reset_bandit_state(self): """ Restablece el estado posterior para una nueva ejecución de muestreo de Thompson. """ self.alpha = np.full(self.n_arms, self.alpha_prior, dtype=float) self.beta = np.full(self.n_arms, self.beta_prior, dtype=float)
Funciones de selección y recompensa
Comenzando con posterior_means(), podemos usar esta función para devolver la tasa de apertura realizada para cualquier título determinado. La siguiente función, select_headline(), muestra un valor aleatorio de la parte posterior de un título y devuelve el índice del valor más grande. Finalmente, tenemos update_posterior(), que incrementa alfa o beta para un título seleccionado según la recompensa.
def posterior_means(self): """ Devuelve la media posterior para cada título. """ return self.alpha / (self.alpha + self.beta) def select_headline(self): """ Extrae una muestra de la Beta posterior de cada brazo y selecciona el título con el valor de muestra más alto. """ sampled_values = self.rng.beta(self.alpha, self.beta) return int(np.argmax(sampled_values)) def update_posterior(self, arm_index, recompensa): """ Actualiza la Beta posterior del brazo seleccionado usando la recompensa observada. """ if arm_index < 0 o arm_index >= self.n_arms: rise IndexError("arm_index está fuera de límites.") si la recompensa no está en (0, 1): rise ValueError("la recompensa debe ser 0 o 1.") self.alpha[arm_index] += recompensa self.beta[arm_index] += (1 – recompensa)
ejecutar() y build_summary_table()
Todo está listo para ejecutar una simulación basada en Thompson Sampling. Tenga en cuenta que llamamos a reset_results() y reset_bandit_state() para asegurarnos de tener una ejecución nueva, para no depender de información anterior. Al final de cada simulación, los resultados se agregan y resumen mediante la función personalizada build_summary_table().
def run(self, num_iterations): """ Ejecute una nueva simulación de Thompson Sampling desde cero. Parámetros ———- num_iterations: int Número de envíos de correo electrónico simulados. """ si num_iterations <= 0: rise ValueError("num_iterations debe ser mayor que 0.") self.reset_results() self.reset_bandit_state() records =[]cumulative_opens = 0 para round_number en el rango (1, num_iterations + 1): arm_index = self.select_headline() recompensa = self.send_email(arm_index) self.update_posterior(arm_index, recompensa) cumulative_opens += recompensa records.append({ "round": round_number, "arm_index": arm_index, "headline": self.headlines[arm_index], "reward": recompensa, "true_open_rate": self.true_probabilities[arm_index], "cumulative_opens": cumulative_opens, "posterior_mean": self.posterior_means()[arm_index], "alpha": self.alpha[arm_index], "beta": self.beta[arm_index] }) self._finalize_history(records) # Reconstruir la tabla de resumen con columnas posteriores adicionales self.summary_table = self.build_summary_table() def build_summary_table(self): """ Construir una tabla de resumen para la última ejecución de Thompson Sampling. """ if self.history.empty: return pd.DataFrame(columns=[ "arm_index", "headline", "selections", "opens", "realized_open_rate", "true_open_rate", "final_posterior_mean", "final_alpha", "final_beta" ]) resumen = ( self.history .groupby(["arm_index", "headline"], as_index=False) .agg( selecciones=("recompensa", "tamaño"), opens=("recompensa", "suma"), realizado_open_rate=("recompensa", "media"), true_open_rate=("true_open_rate", "first") ) .sort_values("arm_index") .reset_index(drop=True) ) resumen["final_posterior_mean"] = self.posterior_means() resumen["final_alpha"] = self.alpha resumen["final_beta"] = self.beta devolver resumen
Ejecutando la simulación
en Unsplash. De uso gratuito bajo la licencia Unsplash
Un último paso antes de ejecutar la simulación, eche un vistazo a una función personalizada que creé específicamente para este paso. Esta función ejecuta varias simulaciones dada una lista de iteraciones. También genera un resumen detallado que compara directamente los enfoques aleatorio y bandido, mostrando específicamente métricas clave como las aperturas de correo electrónico adicionales por parte del bandido, las tasas de apertura generales y la elevación entre la tasa de apertura bandido y la tasa de apertura aleatoria.
def run_comparison_experiment( titulares, true_probabilities, iteration_list=(100, 1000, 10000, 100000, 1000000), random_seed=42, bandit_seed=123, alpha_prior=1.0, beta_prior=1.0 ): """ Ejecute RandomSimulation y BanditSimulation uno al lado del otro en múltiples recuentos de iteraciones. Devuelve ——- compare_df: pd.DataFrame Tabla de comparación de alto nivel en recuentos de iteraciones.[]resultados_detallados = {} for n en iteration_list: # Objetos nuevos para cada tamaño de simulación random_sim = RandomSimulation( titulares=titulares, true_probabilities=true_probabilities, random_state=random_seed ) bandit_sim = BanditSimulation( titulares=headlines, true_probabilities=true_probabilities, alpha_prior=alpha_prior, beta_prior=beta_prior, random_state=bandit_seed ) # Ejecutar ambas simulaciones random_sim.run(num_iterations=n) bandit_sim.run(num_iterations=n) # Métricas principales random_opens = random_sim.total_opens bandit_opens = bandit_sim.total_opens random_open_rate = random_opens / n bandit_open_rate = bandit_opens / n adicional_opens = bandit_opens – random_opens opens_lift_pct = ( ((bandit_opens – random_opens) / random_opens) * 100 si random_opens != 0 else np.nan ) open_rate_lift_pct = ( ((bandit_open_rate – random_open_rate) / random_open_rate) * 100 si random_open_rate != 0 else np.nan ) compare_rows.append({ "iterations": n, "random_opens": random_opens, "bandit_opens": bandit_opens, "additional_opens_from_bandit": advanced_opens, "opens_lift_pct": opens_lift_pct, "random_open_rate": random_open_rate, "bandit_open_rate": bandit_open_rate, "open_rate_lift_pct": open_rate_lift_pct }) resultados_detallados[n] = { "random_sim": random_sim, "bandit_sim": bandit_sim, "random_summary_table": random_sim.summary_table.copy(), "bandit_summary_table": bandit_sim.summary_table.copy() } comparación_df = pd.DataFrame(comparison_rows) # Ayudas de formato opcionales comparacion_df["random_open_rate"] = comparacion_df["random_open_rate"].round(4) comparacion_df["bandit_open_rate"] = comparacion_df["bandit_open_rate"].round(4) comparacion_df["opens_lift_pct"] = comparación_df["opens_lift_pct"].round(2) comparación_df["open_rate_lift_pct"] = comparación_df["open_rate_lift_pct"].round(2) devuelve comparación_df, resultados_detallados
Revisar los resultados
Aquí está el código para ejecutar ambas simulaciones y la comparación, junto con un conjunto de titulares de correo electrónico y la tasa de apertura real correspondiente. ¡Veamos cómo se desempeñó el bandido!
headlines = [ "Solo 48 horas: Ahorre 25%", "Su oferta exclusiva de primavera está aquí", "No se pierda su descuento para miembros", "Termina esta noche: última oportunidad de ahorrar", "Algo solo para usted" ] true_open_rates = [0.18, 0.21, 0.16, 0.24, 0.20] compare_df, detallado_resultados = run_comparison_experiment( titulares = titulares, probabilidades_verdaderas = tasas_de_apertura_verdaderas, lista_de_iteraciones = (100, 1000, 10000, 100000, 1000000), semillas_aleatorias = 42, semillas_bandidas = 123 ) display_df = comparación_df.copy() display_df["random_open_rate"] = (display_df["random_open_rate"] * 100).round(2).astype(str) + "%" display_df["bandit_open_rate"] = (display_df["bandit_open_rate"] * 100).round(2).astype(str) + "%" display_df["opens_lift_pct"] = display_df["opens_lift_pct"].round(2).astype(str) + "%" display_df["open_rate_lift_pct"] = display_df["open_rate_lift_pct"].round(2).astype(str) + "%" display_df
A las 100 iteraciones, no existe una diferencia real entre los dos enfoques. En 1.000, es un resultado similar, excepto que el enfoque de bandido está rezagado esta vez. Ahora mire lo que sucede en las últimas tres iteraciones con 10.000 o más: ¡el enfoque bandido supera consistentemente en un 20%! Puede que ese número no parezca mucho; sin embargo, imagine que es para una gran empresa que puede enviar millones de correos electrónicos en una sola campaña. Ese 20% podría generar millones de dólares en ingresos incrementales.
Mis pensamientos finales
El enfoque de Thompson Sampling ciertamente puede ser una herramienta poderosa en el mundo digital, particularmente como una alternativa de prueba A/B en línea para campañas y recomendaciones. Dicho esto, tiene el potencial de funcionar mucho mejor en algunos escenarios que en otros. Para concluir, aquí hay una lista de verificación rápida que se puede utilizar para determinar si un enfoque de muestreo Thompson podría resultar valioso:
Un KPI único y claro El enfoque depende de un único resultado para las recompensas; por lo tanto, cualquiera que sea la actividad subyacente, la métrica de éxito de esa actividad debe tener un resultado único y claro para que se considere exitosa. Un mecanismo de recompensa casi instantáneo El mecanismo de recompensa debe ser entre casi instantáneo y en cuestión de minutos una vez que la actividad se imprime en el cliente o usuario. Esto permite que el algoritmo reciba comentarios rápidamente, optimizando así más rápido. Ancho de banda o presupuesto para una gran cantidad de iteraciones. Este no es un número mágico de cuántos envíos de correo electrónico, visitas a páginas, impresiones, etc., se debe lograr para tener una actividad de Thompson Sampling efectiva; sin embargo, si vuelve a consultar los resultados de la simulación, cuanto más grande, mejor. Brazos múltiples y distintos Brazos, como metáfora del problema de los bandidos, cualquiera que sea la experiencia, las variaciones, como los titulares de los correos electrónicos, deben ser distintas o tener una alta variabilidad para garantizar que se maximice el espacio de exploración. Por ejemplo, si está probando el color de una página de destino, en lugar de probar diferentes tonos de un solo color, considere probar colores completamente diferentes.
¡Espero que hayas disfrutado de mi introducción y simulación con Thompson Sampling y el problema Multi-Armed Bandit! Si puede encontrar una salida adecuada para ello, puede que le resulte extremadamente valioso.