El muestreo de reservorio es una familia de algoritmos aleatorios para seleccionar una muestra aleatoria simple , sin reemplazo, de k elementos de una población de tamaño desconocido n en una sola pasada. El tamaño de la población n es desconocido para el algoritmo y suele ser demasiado grande para que todos los n elementos quepan en la memoria principal . La población se revela al algoritmo con el tiempo, y este no puede consultar elementos anteriores. En cualquier momento, el estado actual del algoritmo debe permitir la extracción de una muestra aleatoria simple sin reemplazo de tamaño k sobre la parte de la población vista hasta el momento.
Motivación
Supongamos que vemos una secuencia de elementos, uno a la vez. Queremos almacenar 10 elementos en memoria y seleccionarlos aleatoriamente de la secuencia. Si conocemos el número total de elementos n y podemos acceder a ellos arbitrariamente, la solución es sencilla: seleccionar 10 índices distintos i entre 1 y n con igual probabilidad y conservar el i -ésimo elemento. El problema es que no siempre conocemos el valor exacto de n de antemano.
Simple: Algoritmo R
Jeffrey Vitter creó un algoritmo sencillo y popular, pero lento, llamado Algoritmo R. [ 1 ]
Inicializar un arrayindexado desdea, que contiene los primeros k elementos de la entradaEste es el embalse .
Para cada nueva entrada, generar un número aleatorio j uniformemente en. Si, luego estableceDe lo contrario, deséchelo..
Devolveruna vez que se hayan procesado todas las entradas.
Este algoritmo funciona por inducción en.
CuandoEl algoritmo R devuelve todas las entradas, proporcionando así la base para una demostración por inducción matemática.
Aquí, la hipótesis de inducción es que la probabilidad de que una entrada particular se incluya en el reservorio justo antes de la-la entrada se procesa esy debemos demostrar que la probabilidad de que una entrada particular se incluya en el reservorio esjusto después de laLa entrada -th es procesada.
Aplicar el algoritmo R al-ª entrada. Entradase incluye con probabilidadpor definición del algoritmo. Para cualquier otra entrada, por la hipótesis de inducción, la probabilidad de que esté incluido en el reservorio justo antes de la-la entrada se procesa es. La probabilidad de que todavía esté incluido en el embalse despuésse procesa (en otras palabras, queno es reemplazado por) esEsto último se deduce de la suposición de que el enterose genera uniformemente al azar; una vez que queda claro que de hecho ocurrirá un reemplazo, la probabilidad de queen particular es reemplazado pores.
Hemos demostrado que la probabilidad de que un nuevo elemento ingrese al reservorio es igual a la probabilidad de que un elemento existente en el reservorio se conserve. Por lo tanto, concluimos, por el principio de inducción matemática, que el algoritmo R produce efectivamente una muestra aleatoria uniforme de los elementos de entrada.
Aunque conceptualmente simple y fácil de entender, este algoritmo necesita generar un número aleatorio para cada elemento de la entrada, incluidos los elementos que se descartan. Por lo tanto , el tiempo de ejecución asintótico del algoritmo esGenerar esta cantidad de aleatoriedad y el tiempo de ejecución lineal hace que el algoritmo sea innecesariamente lento si la población de entrada es grande.
Este es el algoritmo R, implementado de la siguiente manera:
(* S tiene elementos para muestrear, R contendrá el resultado *) ReservoirSample ( S [ 1 .. n ] , R [ 1 .. k ]) // llena el array del reservorio para i := 1 a k R [ i ] := S [ i ] fin// Reemplazar elementos con probabilidad decreciente gradualmente para i := k + 1 hasta n (* randomInteger(a, b) genera un entero uniforme del rango inclusivo {a, ..., b} *) j := randomInteger ( 1 , i ) si j <= k R [ j ] := S [ i ] fin fin finÓptimo: Algoritmo L
Si generamosnúmeros aleatoriosindependientemente, entonces los índices del más pequeñode ellos es una muestra uniforme de la-subconjuntos de.
El proceso se puede realizar sin saberlo:
Conserva el más pequeñodeque se ha visto hasta ahora, así como, el índice del mayor entre ellos. Para cada nuevo, compáralo con. Siluego deséchelo, almacenary establecerser el índice del mayor entre ellos. De lo contrario, descartary establecer.
Ahora combine esto con el flujo de entradasCada vez que alguienSi se acepta, almacene el correspondienteCada vez que alguiense descarta, deseche el correspondiente.
Este algoritmo aún necesitanúmeros aleatorios, tomando asítiempo. Pero se puede simplificar.
Primera simplificación: no es necesario probar nuevosuno por uno, ya que la probabilidad de que la próxima aceptación ocurra enes, es decir, el intervaloLa tasa de aceptación sigue una distribución geométrica .
Segunda simplificación: no es necesario recordar toda la matriz de los más pequeños.deeso es lo que se ha visto hasta ahora, pero simplemente, la más grande de ellas. Esto se basa en tres observaciones:
- Cada vez que hay algo nuevoSi se selecciona una entrada para ser almacenada, se descarta una entrada aleatoria uniforme en el almacenamiento.
- tiene la misma distribución quedonde todosde forma independiente. Esto se puede muestrear primero mediante muestreo., luego tomando.
Este es el algoritmo L , [ 2 ] que se implementa de la siguiente manera:
(* S tiene elementos para muestrear, R contendrá el resultado *) ReservoirSample ( S [ 1 .. n ] , R [ 1 .. k ]) // llena el array del reservorio para i = 1 a k R [ i ] := S [ i ] fin(* random() genera un número aleatorio uniforme (0,1) *) W := exp ( log ( random ()) / k )mientras i <= n i := i + floor ( log ( random ()) / log ( 1 - W )) + 1 si i <= n (* reemplazar un elemento aleatorio del depósito con el elemento i *) R [ randomInteger ( 1 , k )] := S [ i ] // índice aleatorio entre 1 y k, inclusive W := W * exp ( log ( random ()) / k ) fin fin finEste algoritmo calcula tres números aleatorios para cada elemento que pasa a formar parte del reservorio y no dedica tiempo a los elementos que no lo hacen. Por lo tanto, su tiempo de ejecución esperado es:, [ 2 ] que es óptimo. [ 1 ] Al mismo tiempo, es sencillo de implementar de manera eficiente y no depende de desviaciones aleatorias de distribuciones exóticas o difíciles de calcular.
Con orden aleatorio
Si asociamos a cada elemento de la entrada un número aleatorio generado uniformemente, los k elementos con los valores asociados más grandes (o, equivalentemente, más pequeños) forman una muestra aleatoria simple. [ 3 ] Un muestreo de reservorio simple mantiene así los k elementos con los valores asociados más grandes actualmente en una cola de prioridad .
(* S es un flujo de elementos para muestrear S.Current devuelve el elemento actual en el flujo S.Next avanza el flujo a la siguiente posición min-priority-queue admite: Count -> número de elementos en la cola de prioridad Minimum -> devuelve el valor de clave mínimo de todos los elementos Extract-Min() -> Elimina el elemento con la clave mínima Insert(key, Item) -> Agrega el elemento con la clave especificada *) ReservoirSample ( S [ 1 .. ? ]) H := nueva min - priority - queue mientras S tenga datos r := random () // aleatorio uniforme entre 0 y 1, exclusivo si H . Count < k H . Insert ( r , S . Current ) else // conserva k elementos con las claves asociadas más grandes si r > H . Minimum H . Extract - Min () H . Insert ( r , S . Current ) end S . Next end devuelve los elementos en H endEl tiempo de ejecución previsto de este algoritmo esY es relevante principalmente porque se puede extender fácilmente a artículos con peso.
Muestreo aleatorio ponderado
Los métodos presentados en las secciones anteriores no permiten obtener probabilidades de inclusión fijas a priori. Algunas aplicaciones requieren que las probabilidades de muestreo de los elementos se ajusten a los pesos asociados a cada elemento. Por ejemplo, podría ser necesario muestrear consultas en un motor de búsqueda con un peso igual al número de veces que se realizaron, de modo que la muestra pueda analizarse para determinar su impacto general en la experiencia del usuario. Sea el peso del elemento i .y la suma de todos los pesos sea W. Hay dos maneras de interpretar los pesos asignados a cada elemento del conjunto: [ 4 ]
- En cada ronda, la probabilidad de que cada elemento no seleccionado sea seleccionado en esa ronda es proporcional a su peso en relación con los pesos de todos los elementos no seleccionados. Si X es la muestra actual, entonces la probabilidad de que un elemento sea seleccionado es proporcional a su peso en relación con los pesos de todos los elementos no seleccionados.ser seleccionado en la ronda actual es.
- La probabilidad de que cada elemento se incluya en la muestra aleatoria es proporcional a su peso relativo, es decir,No es posible lograrlo cuando, puesto que cada elemento se incluye entonces con una probabilidad de 1.
Algoritmo A-Res
El siguiente algoritmo fue proporcionado por Efraimidis y Spirakis que utiliza la interpretación 1: [ 5 ]
(* S es un flujo de elementos para muestrear S.Current devuelve el elemento actual en el flujo S.Weight devuelve el peso del elemento actual en el flujo S.Next avanza el flujo a la siguiente posición El operador de potencia está representado por ^ min-priority-queue admite: Count -> número de elementos en la cola de prioridad Minimum() -> devuelve el valor de clave mínimo de todos los elementos Extract-Min() -> Elimina el elemento con la clave mínima Insert(key, Item) -> Agrega el elemento con la clave especificada *) ReservoirSample ( S [ 1 .. ? ]) H := nueva min - priority - queue mientras S tiene datos r := random () ^ ( 1 / S . Weight ) // random() produce un número aleatorio uniforme en (0,1) si H . Count < k H . Insert ( r , S . Current ) else // conserva k elementos con las claves asociadas más grandes si r > H . Minimum H . Extract - Min () H . Insert ( r , S . Current ) end end S . A continuación , devuelva los artículos al final H.Este algoritmo es idéntico al algoritmo presentado en Reservoir Sampling with Random Sort, excepto por la generación de las claves de los elementos. El algoritmo es equivalente a asignar una clave a cada elemento.donde r es el número aleatorio y luego seleccionando los k elementos con las claves más grandes. De forma equivalente, una formulación numéricamente más estable de este algoritmo calcula las claves comoy seleccionar los k elementos con las claves más pequeñas . [ 6 ]
Algoritmo A-ExpJ
El siguiente algoritmo es una versión más eficiente de A-Res , también proporcionada por Efraimidis y Spirakis: [ 5 ]
(* S es un flujo de elementos para muestrear S.Current devuelve el elemento actual en el flujo S.Weight devuelve el peso del elemento actual en el flujo S.Next avanza el flujo a la siguiente posición El operador de potencia está representado por ^ min-priority-queue admite: Count -> número de elementos en la cola de prioridad Minimum -> clave mínima de cualquier elemento en la cola de prioridad Extract-Min() -> Elimina el elemento con la clave mínima Insert(Key, Item) -> Agrega el elemento con la clave especificada *) ReservoirSampleWithJumps ( S [ 1 .. ? ]) H := nueva min - priority - queue mientras S tiene datos y H . Count < k r := random () ^ ( 1 / S . Weight ) // random() produce un número aleatorio uniforme en (0,1) H . Insert ( r , S . Current ) S . Siguiente fin X := log ( aleatorio ()) / log ( H . Mínimo ) // esta es la cantidad de peso que se debe saltar mientras S tiene datos X := X - S . Peso si X <= 0 t := H . Mínimo ^ S . Peso r := aleatorio ( t , 1 ) ^ ( 1 / S . Peso ) // aleatorio(x, y) produce un número aleatorio uniforme en (x, y) H . Extraer - Mínimo () H . Insertar ( r , S . Actual )X := log ( aleatorio ()) / log ( H . Mínimo ) fin S . Siguiente fin devolver elementos en H finEste algoritmo sigue las mismas propiedades matemáticas que se utilizan en A-Res , pero en lugar de calcular la clave para cada elemento y comprobar si ese elemento debe insertarse o no, calcula un salto exponencial al siguiente elemento que se insertará. Esto evita tener que crear variables aleatorias para cada elemento, lo que puede resultar costoso. El número de variables aleatorias requeridas se reduce deaen expectativa, dondees el tamaño del depósito, yes el número de elementos en el flujo. [ 5 ]
Algoritmo A-Chao
Advertencia: la siguiente descripción es incorrecta; consulte el artículo original de Chao y la discusión aquí .
El siguiente algoritmo fue dado por MT Chao usa la interpretación 2: [ 7 ] y Tillé (2006). [ 8 ]
(* S tiene elementos para muestrear, R contendrá el resultado S[i].Weight contiene el peso para cada elemento *) WeightedReservoir - Chao ( S [ 1 .. n ] , R [ 1 .. k ]) WSum := 0 // llenar el array del reservorio para i := 1 a k R [ i ] := S [ i ] WSum := WSum + S [ i ] . Weight fin para i := k + 1 a n WSum := WSum + S [ i ] . Weight p := S [ i ] . Weight / WSum // probabilidad para este elemento j := random () ; // aleatorio uniforme entre 0 y 1 si j <= p // seleccionar elemento según la probabilidad R [ randomInteger ( 1 , k )] := S [ i ] // selección uniforme en el reservorio para reemplazo fin fin finPara cada elemento, se calcula su peso relativo y se utiliza para decidir aleatoriamente si se añadirá al depósito. Si se selecciona el elemento, se elige uniformemente uno de los elementos existentes en el depósito y se reemplaza por el nuevo. La clave está en que, si las probabilidades de todos los elementos en el depósito ya son proporcionales a sus pesos, al seleccionar uniformemente qué elemento reemplazar, las probabilidades de todos los elementos siguen siendo proporcionales a su peso después del reemplazo.
Nótese que Chao no especifica cómo muestrear los primeros k elementos. Simplemente asume que tenemos alguna otra forma de seleccionarlos en proporción a su peso. Chao: "Supongamos que tenemos un plan de muestreo de tamaño fijo con respecto a S_k en el instante A ; tal que su probabilidad de inclusión de primer orden de X_t es π(k; i) ".
Algoritmo A-Chao con saltos
De forma similar a otros algoritmos, es posible calcular un peso aleatorio jy restar los valores de masa de probabilidad de los elementos, omitiéndolos mientras j > 0, reduciendo así la cantidad de números aleatorios que deben generarse. [ 4 ]
(* S tiene elementos para muestrear, R contendrá el resultado S[i].Weight contiene el peso para cada elemento *) WeightedReservoir - Chao ( S [ 1 .. n ] , R [ 1 .. k ]) WSum := 0 // llenar el arreglo del reservorio para i := 1 a k R [ i ] := S [ i ] WSum := WSum + S [ i ] . Weight end j := random () // uniformemente aleatorio entre 0 y 1 pNone := 1 // probabilidad de que no se haya seleccionado ningún elemento hasta ahora (en este salto) para i := k + 1 a n WSum := WSum + S [ i ] . Weight p := S [ i ] . Peso / WSum // probabilidad para este elemento j -= p * pNone pNone := pNone * ( 1 - p ) si j <= 0 R [ randomInteger ( 1 , k )] := S [ i ] //selección uniforme en el reservorio para reemplazo j = random () pNone := 1 fin fin finAplicaciones para la equidad multiclase
En las aplicaciones de aprendizaje automático , la equidad es un factor crítico, especialmente en escenarios donde los flujos de datos presentan desequilibrio de clases. Para abordar este problema, Nikoloutsopoulos, Titsias y Koutsopoulos propusieron el algoritmo de muestreo de reservorio de Kullback-Leibler (KLRS) como solución a los desafíos del aprendizaje continuo, donde los modelos deben aprender de forma incremental a partir de un flujo de datos continuo.
Algoritmo KLRS
El algoritmo KLRS se diseñó para crear una política flexible que ajuste los porcentajes de clase en el búfer a una distribución objetivo, empleando técnicas de muestreo de reservorio. Esto se logra minimizando la divergencia de Kullback-Leibler (KL) entre la distribución actual del búfer y la distribución objetivo deseada.
KLRS generaliza métodos anteriores como el muestreo de embalses y el muestreo de embalses con equilibrio de clases, como lo verifican los experimentos que utilizan intervalos de confianza, lo que demuestra su mayor aplicabilidad y su rendimiento mejorado.
Descripción del algoritmo
El algoritmo KLRS funciona manteniendo un búfer de tamaño y actualizando su contenido a medida que llegan nuevos puntos de datos en un flujo. A continuación se muestra el pseudocódigo del algoritmo KLRS:
Algoritmo: KLRS
KLRS ( Flujo , Tamaño del búfer M , Distribución objetivo ) Entrada : * Flujo ( puntos de datos ( x , y ) que llegan secuencialmente ) * Tamaño del búfer M * Distribución objetivo ( distribución de clase deseada en el búfer )Salida : Búfer actualizado manteniendo la distribución objetivo .Inicializa el búfer M con los primeros M puntos de datos del flujo . Para t = M + 1 a ∞ : - Actualiza los contadores del flujo para calcular n_ {t+1} . - Calcula la distribución objetivo p_ {t+1} . - Para cada clase k en el búfer : - Simula la eliminación de una instancia de la clase k y la adición de ( x_t , y_t ) . - Calcula la divergencia KL v_k entre p_ {t+1} y la distribución del búfer modificada . - Selecciona la clase k * con el mínimo v_k . - Si k * = y_t : - Genera un número aleatorio r en [ 1 , n_ {t+1} , y_t ] . - Si r ≤ m_ {t, y_t} , - reemplaza una instancia aleatoria de la clase y_t en el búfer con ( x_t , y_t ) . - De lo contrario , reemplace una instancia aleatoria de la clase seleccionada k * en el búfer con ( x_t , y_t ) . - Actualice los contadores del búfer m_ {t+1}]. - Actualice el conjunto de clases C_{t+1} := unique ( C_t ∪ {y_t} ) .Relación con la reorganización de Fisher-Yates
Supongamos que se quisiera extraer k cartas al azar de una baraja. Un enfoque natural sería barajar la baraja y luego tomar las k primeras cartas. En el caso general, la barajada también debe funcionar incluso si no se conoce de antemano el número de cartas en la baraja, una condición que cumple la versión de adentro hacia afuera de la barajada de Fisher-Yates : [ 9 ]
(* S contiene la entrada, R contendrá la permutación de salida *) Barajar ( S [ 1 .. n ] , R [ 1 .. n ]) R [ 1 ] := S [ 1 ] para i desde 2 hasta n hacer j := randomInteger ( 1 , i ) // rango inclusivo R [ i ] := R [ j ] R [ j ] := S [ i ] fin finNótese que, si bien el resto de las cartas se barajan, solo las primeras k son importantes en este contexto. Por lo tanto, el arreglo R solo necesita registrar las cartas en las primeras k posiciones durante el barajado, lo que reduce la cantidad de memoria necesaria. Al truncar R a una longitud k , el algoritmo se modifica en consecuencia:
(* S tiene elementos para muestrear, R contendrá el resultado *) ReservoirSample ( S [ 1 .. n ] , R [ 1 .. k ]) R [ 1 ] := S [ 1 ] para i desde 2 hasta k hacer j := randomInteger ( 1 , i ) // rango inclusivo R [ i ] := R [ j ] R [ j ] := S [ i ] fin para i desde k + 1 hasta n hacer j := randomInteger ( 1 , i ) // rango inclusivo si ( j <= k ) R [ j ] := S [ i ] fin fin finDado que el orden de las primeras k cartas es irrelevante, se puede eliminar el primer bucle y R puede inicializarse con los primeros k elementos de la entrada. Esto da como resultado el algoritmo R.
Limitaciones
El muestreo de reservorio parte del supuesto de que la muestra deseada cabe en la memoria principal , lo que a menudo implica que k es una constante independiente de n . En aplicaciones donde nos gustaría seleccionar un subconjunto grande de la lista de entrada (por ejemplo, un tercio, es decir)), es necesario adoptar otros métodos. Se han propuesto implementaciones distribuidas para este problema. [ 10 ]
Referencias
- ^ a b Vitter, Jeffrey S. (1 de marzo de 1985). "Muestreo aleatorio con un reservorio" (PDF) . ACM Transactions on Mathematical Software . 11 (1): 37– 57. CiteSeerX 10.1.1.138.784 . doi : 10.1145/3147.3165 . S2CID 17881708 .
- ^ a b Li, Kim-Hung (4 de diciembre de 1994). "Algoritmos de muestreo de reservorio con complejidad temporal O(n(1+log(N/n)))" . ACM Transactions on Mathematical Software . 20 (4): 481– 493. doi : 10.1145/198429.198435 . S2CID 15721242 .
- ^ Fan, C.; Muller, ME; Rezucha, I. (1962). "Desarrollo de planes de muestreo mediante técnicas de selección secuencial (elemento por elemento) y computadoras digitales". Journal of the American Statistical Association . 57 (298): 387– 402. doi : 10.1080/01621459.1962.10480667 . JSTOR 2281647 .
- ^ a b Efraimidis, Pavlos S. (2015). "Muestreo aleatorio ponderado sobre flujos de datos". Algoritmos, probabilidad, redes y juegos . Notas de clase en ciencias de la computación. Vol. 9295. págs. 183–195 . arXiv : 1012.0256 . doi : 10.1007/978-3-319-24024-4_12 . ISBN 978-3-319-24023-7. S2CID 2008731 .
- ^ a b c Efraimidis, Pavlos S.; Spirakis, Paul G. (2006-03-16). "Muestreo aleatorio ponderado con un reservorio". Information Processing Letters . 97 (5): 181– 185. doi : 10.1016/j.ipl.2005.11.003 .
- ^ Arratia, Richard (2002). Bela Bollobas (ed.). "Sobre la cantidad de dependencia en la factorización prima de un entero aleatorio uniforme". Combinatoria Contemporánea . 10 : 29–91 . CiteSeerX 10.1.1.745.3975 . ISBN 978-3-642-07660-2.
- ^ Chao, MT (1982). "Un plan de muestreo de probabilidad desigual de propósito general". Biometrika . 69 (3): 653– 656. doi : 10.1093/biomet/69.3.653 .
- ^ Tillé, Yves (2006). Algoritmos de muestreo . Springer. ISBN 978-0-387-30814-2.
- ^ Consejo Nacional de Investigación (2013). Fronteras en el análisis de datos masivos . The National Academies Press. pág. 121. ISBN 978-0-309-28781-4.
- Muestreo de embalses en MapReduce
- Algoritmos
- Análisis de algoritmos
- Algoritmos aleatorios