Productos secos 丨 Tutorial de computación general de DolphinDB para la base de datos de series de tiempo

DolphinDB no solo puede almacenar datos distribuidos, sino que también tiene un buen soporte para la computación distribuida. En DolphinDB, los usuarios pueden utilizar el marco de computación distribuida general proporcionado por el sistema para implementar algoritmos distribuidos eficientes a través de scripts sin prestar atención a implementaciones subyacentes específicas. Este artículo proporcionará una explicación detallada de los conceptos importantes y las funciones relacionadas en el marco de trabajo informático general de DolphinDB, y proporcionará una gran cantidad de escenarios y ejemplos de uso específicos.


1. Fuente de datos

Data Source es el concepto básico en el marco informático general de la base de datos DolphinDB. Es un tipo especial de objeto de datos, que es una meta descripción de datos. Al ejecutar la fuente de datos, los usuarios pueden obtener entidades de datos como tablas, matrices y vectores. En el marco de computación distribuida de DolphinDB, se transmiten objetos de origen de datos livianos en lugar de entidades de datos enormes a nodos remotos para cálculos posteriores, lo que reduce en gran medida el tráfico de red.

En DolphinDB, los usuarios suelen utilizar la función sqlDS para generar una fuente de datos basada en una expresión SQL. Esta función no consulta directamente la tabla, sino que devuelve la metadeclaración de una o más subconsultas SQL, es decir, la fuente de datos. Después de eso, los usuarios pueden usar el marco Map-Reduce para ingresar fuentes de datos y funciones de cálculo, distribuir tareas a los nodos correspondientes a cada fuente de datos, completar los cálculos en paralelo y luego resumir los resultados.

En las secciones 3.1, 3.2, 3.3 y 3.4 de este artículo se describirán en detalle varios métodos comúnmente utilizados para obtener fuentes de datos.


2. Marco Map-Reduce

La función Map-Reduce es la función principal del marco de computación distribuida general de DolphinDB.

2.1 función mr

La sintaxis de la función Map-Reduce mr de DolphinDB es mr (ds, mapFunc, [reduceFunc], [finalFunc], [paralelo = true]), que acepta un conjunto de fuentes de datos y una función mapFunc como parámetros. Distribuirá las tareas informáticas al nodo donde se encuentra cada fuente de datos y procesará los datos en cada fuente de datos a través de mapFunc. El parámetro opcional reduceFunc calculará el valor de retorno de mapFunc en pares, y el resultado obtenido se calculará con el valor de retorno del tercer mapFunc, de modo que el cálculo acumulativo resumirá los resultados de mapFunc. Si hay M llamadas al mapa, la función de reducción se llamará M-1 veces. El parámetro opcional finalFunc procesa aún más el valor de retorno de reduceFunc.

Hay un ejemplo de regresión lineal de mínimos cuadrados distribuidos mediante mr en el documento oficial . Este artículo usa el siguiente ejemplo para mostrar cómo usar una llamada mr para muestrear aleatoriamente una décima parte de los datos en cada partición de la tabla distribuida:

// 创建数据库和DFS表
db = database("dfs://sampleDB", VALUE, `a`b`c`d)
t = db.createPartitionedTable(table(100000:0, `sym`val, [SYMBOL,DOUBLE]), `tb, `sym)
n = 3000000
t.append!(table(rand(`a`b`c`d, n) as sym, rand(100.0, n) as val))

// 定义map函数
def sampleMap(t) {
    sampleRate = 0.1
    rowNum = t.rows()
    sampleIndex = (0..(rowNum - 1)).shuffle()[0:int(rowNum * sampleRate)]
    return t[sampleIndex]
}

ds = sqlDS(<select * from t>)              // 创建数据源
res = mr(ds, sampleMap, , unionAll)        // 执行计算

En el ejemplo anterior, la función sampleMap definida por el usuario acepta una tabla (es decir, los datos de la fuente de datos) como parámetro y devuelve aleatoriamente una décima parte de las filas. La función mr en este ejemplo no tiene un parámetro reduceFunc, por lo que el valor de retorno de cada función de mapa se coloca en una tupla y se pasa a finalFunc, es decir, unionAll. unionAll fusiona varias tablas devueltas por la función de mapa en una tabla distribuida con particiones secuenciales.

2.2 función imr

La base de datos DolphinDB proporciona una función de cálculo iterativo imr basada en el método Map-Reduce. En comparación con mr, puede admitir cálculos iterativos, cada iteración utiliza el resultado de la iteración anterior y el conjunto de datos de entrada, por lo que puede admitir la realización de algoritmos más complejos. El cálculo iterativo requiere los valores iniciales y los criterios de terminación de los parámetros del modelo. Su sintaxis es imr (ds, initValue, mapFunc, [reduceFunc], [finalFunc], terminateFunc, [carryover = false]) donde el parámetro initValue es el valor inicial de la primera iteración, el parámetro mapFunc es una función y el valor aceptado Los parámetros incluyen la entidad de origen de datos y la salida de la función final en la iteración anterior. Para la primera iteración, es el valor inicial dado por el usuario. Los parámetros en imr son similares a la función mr. La función finalFunc acepta dos parámetros, el primer parámetro es la salida de la función final en la iteración anterior. Para la primera iteración, es el valor inicial dado por el usuario. El segundo parámetro es la salida después de llamar a la función de reducción. El parámetro terminateFunc se utiliza para determinar si la iteración finaliza. Acepta dos parámetros. La primera es la salida de la función de reducción en la iteración anterior y la segunda es la salida de la función de reducción en la iteración actual. Si devuelve verdadero, la iteración se cancelará. El parámetro de transferencia indica si la llamada a la función de mapa genera un objeto para pasar a la siguiente llamada a la función de mapa. Si la transferencia es verdadera, entonces la función de mapa tiene 3 parámetros y el último parámetro es el objeto transportado, y el resultado de salida de la función de mapa es una tupla, y el último elemento es el objeto transportado. En la primera iteración, el objeto transportado es NULL.

Hay un ejemplo de cálculo de la mediana de datos distribuidos mediante imr en el documento oficial . Este artículo proporcionará un ejemplo más complicado, es decir, el cálculo de la regresión logística utilizando el método de Newton para mostrar la aplicación de imr en algoritmos de aprendizaje automático.

def myLrMap(t, lastFinal, yColName, xColNames, intercept) {
	placeholder, placeholder, theta = lastFinal
    if (intercept)
        x = matrix(t[xColNames], take(1.0, t.rows()))
    else
        x = matrix(t[xColNames])
    xt = x.transpose()
    y = t[yColName]
    scores = dot(x, theta)
    p = 1.0 \ (1.0 + exp(-scores))
    err = y - p
    w = p * (1.0 - p)
    logLik = (y * log(p) + (1.0 - y) * log(1.0 - p)).flatten().sum()
    grad = xt.dot(err)                   // 计算梯度向量
    wx = each(mul{w}, x)
    hessian = xt.dot(wx)                 // 计算Hessian矩阵
    return [logLik, grad, hessian]
}

def myLrFinal(lastFinal, reduceRes) {
    placeholder, placeholder, theta = lastFinal
    logLik, grad, hessian = reduceRes
    deltaTheta = solve(hessian, grad)    // deltaTheta等于hessian^-1 * grad,相当于解方程hessian * deltaTheta = grad
    return [logLik, grad, theta + deltaTheta]
}

def myLrTerm(prev, curr, tol) {
	placeholder, grad, placeholder = curr
	return grad.flatten().abs().max() <= tol
}

def myLr(ds, yColName, xColNames, intercept, initTheta, tol) {
    logLik, grad, theta = imr(ds, [0, 0, initTheta], myLrMap{, , yColName, xColNames, intercept}, +, myLrFinal, myLrTerm{, , tol})
    return theta
}

En el ejemplo anterior, la función de mapa calcula el vector de gradiente y la matriz de Hesse bajo los coeficientes actuales para los datos en la fuente de datos; la función de reducción agrega los resultados del mapa, lo que equivale a encontrar el vector de gradiente y la matriz de Hesse del conjunto de datos completo; final La función optimiza los coeficientes a través del vector de gradiente final y la matriz hessiana para completar una ronda de iteración; el criterio para la función de terminación es si el valor absoluto del componente más grande del vector de gradiente en esta ronda de iteración es mayor que el parámetro tol.

Este ejemplo también puede optimizarse aún más para mejorar el rendimiento mediante operaciones de conversión de fuentes de datos; consulte la sección 3.6 para obtener más detalles.

Como herramienta de análisis de uso frecuente, la regresión logística distribuida se ha implementado como una función incorporada en DolphinDB. La versión incorporada ( logisticRegression) proporciona más funciones.


3. Funciones relacionadas con la fuente de datos

DolphinDB proporciona los siguientes métodos de uso común para obtener fuentes de datos:

3.1 función sqlDS

La función sqlDS crea una lista de fuentes de datos basada en el metacódigo SQL de entrada. Si la tabla de datos en la consulta SQL tiene n particiones, sqlDS generará n fuentes de datos. Si la consulta SQL no contiene ninguna tabla particionada, sqlDS devolverá una tupla que contiene solo una fuente de datos.

sqlDS es una forma eficaz de convertir expresiones SQL en fuentes de datos. Los usuarios solo necesitan proporcionar expresiones SQL sin prestar atención a la distribución de datos específica y pueden usar la fuente de datos devuelta para ejecutar algoritmos distribuidos. El ejemplo proporcionado a continuación muestra el método de usar sqlDS para realizar la regresión de mínimos cuadrados distribuidos olsEx en los datos de la tabla DFS.

// 创建数据库和DFS表
db = database("dfs://olsDB", VALUE, `a`b`c`d)
t = db.createPartitionedTable(table(100000:0, `sym`x`y, [SYMBOL,DOUBLE,DOUBLE]), `tb, `sym)
n = 3000000
t.append!(table(rand(`a`b`c`d, n) as sym, 1..n + norm(0.0, 1.0, n) as x, 1..n + norm(0.0, 1.0, n) as y))

ds = sqlDS(<select x, y from t>)    // 创建数据源
olsEx(ds, `y, `x)                   // 执行计算


3.2 función repartitionDS

El sistema genera automáticamente la fuente de datos de sqlDS de acuerdo con la partición de los datos. A veces, los usuarios necesitan establecer algunas restricciones en la fuente de datos, por ejemplo, al obtener datos, volver a especificar la partición de datos para reducir la cantidad de cálculo o solo necesitan una parte de los datos de la partición. La función repartitionDS proporciona la función de reparticionar fuentes de datos.

La función repartitionDS genera una nueva fuente de datos reparticionada para el metacódigo de acuerdo con el metacódigo SQL de entrada y el nombre de columna, tipo de partición, esquema de partición, etc.

El siguiente código proporciona un ejemplo de repartitionDS. En este ejemplo, la tabla DFS t tiene campos deviceId, time, temperature, que son símbolo, fecha y hora y doble respectivamente. La base de datos adopta particiones de doble capa. La primera capa está particionada por VALUE para el tiempo, una partición por día; la segunda La capa es para deviceId Dividida en 20 áreas según HASH.

Ahora es necesario agregar y consultar la temperatura del percentil 95 por el campo deviceId. Si escribe directamente la consulta seleccionar percentil (temperatura, 95) de t grupo por ID de dispositivo, esta consulta no se completará porque la función de percentil no está implementada por Map-Reduce.

Una solución es cargar todos los campos obligatorios localmente y calcular el percentil 95, pero cuando la cantidad de datos es demasiado grande, los recursos informáticos pueden ser insuficientes. repartitionDS proporciona una solución: reparticionar la tabla en función del deviceId de acuerdo con su esquema de partición original HASH, y cada nueva partición corresponde a todos los datos de una partición HASH en la tabla original. Calcule la temperatura del percentil 95 en cada nueva partición mediante la función mr y, finalmente, combine los resultados.

// 创建数据库
deviceId = "device" + string(1..100000)
db1 = database("", VALUE, 2019.06.01..2019.06.30)
db2 = database("", HASH, INT:20)
db = database("dfs://repartitionExample", COMPO, [db1, db2])

// 创建DFS表
t = db.createPartitionedTable(table(100000:0, `deviceId`time`temperature, [SYMBOL,DATETIME,DOUBLE]), `tb, `deviceId`time)
n = 3000000
t.append!(table(rand(deviceId, n) as deviceId, 2019.06.01T00:00:00 + rand(86400 * 10, n) as time, 60 + norm(0.0, 5.0, n) as temperature))

// 重新分区
ds = repartitionDS(<select deviceId, temperature from t>, `deviceId)
// 执行计算
res = mr(ds, def(t) { return select percentile(temperature, 95) from t group by deviceId}, , unionAll{, false})

Se puede garantizar la exactitud del resultado de este cálculo. Debido a que la nueva partición generada por repartitionDS se basa en la partición original de deviceId, puede garantizar que el deviceId de cada fuente de datos no se superponga. Por lo tanto, se puede obtener el resultado correcto combinando los resultados del cálculo de cada partición.


3.3 función textChunkDS

La función textChunkDS puede dividir un archivo de texto en varias fuentes de datos para realizar cálculos distribuidos sobre los datos representados por un archivo de texto. Su sintaxis es: textChunkDS (filename, chunkSize, [delimiter = ','], [schema]). Entre ellos, los parámetros de nombre de archivo, delimitador y esquema son los mismos que los de la función loadText. El parámetro chunkSize indica el tamaño de los datos en cada fuente de datos, en MB, que puede ser un número entero de 1 a 2047.

El siguiente ejemplo es otra implementación del ejemplo de olsEx en el documento oficial . Genera varias fuentes de datos a partir de archivos de texto a través de la función textChunkDS, cada una de las cuales tiene un tamaño de 100 MB. Después de convertir la fuente de datos generada, ejecuta la función olsEx para calcular los parámetros de mínimos cuadrados:

ds = textChunkDS("c:/DolphinDB/Data/USPrices.csv", 100)
ds.transDS!(USPrices -> select VOL\SHROUT as VS, abs(RET) as ABS_RET, RET, log(SHROUT*(BID+ASK)\2) as SBA from USPrices where VOL>0)
rs=olsEx(ds, `VS, `ABS_RET`SBA, true, 2)

Para la operación de conversión de fuente de datos transDS !, consulte la sección 3.6.


3.4 Interfaz de fuente de datos proporcionada por fuentes de datos de terceros

Algunos complementos que cargan datos de terceros, como HDF5, proporcionan una interfaz para generar fuentes de datos. Los usuarios pueden ejecutar directamente algoritmos distribuidos en las fuentes de datos que devuelven sin importar primero datos de terceros en la memoria o guardarlos como discos o tablas distribuidas.

El complemento HDF5 de DolphinDB proporciona la función hdf5DS Los usuarios pueden especificar el número de fuentes de datos que deben generarse configurando el parámetro dsNum. El siguiente ejemplo genera 10 fuentes de datos a partir del archivo HDF5 y calcula la varianza de la muestra en la primera columna del resultado a través del marco Map-Reduce:

ds = hdf5::hdf5DS("large_file.h5", "large_table", , 10)

def varMap(t) {
    column = t.col(0)
    return [column.sum(), column.sum2(), column.count()]
}

def varFinal(result) {
    sum, sum2, count = result
    mu = sum \ count
    populationVar = sum2 \ count - mu * mu
    sampleVar = populationVar * count \ (count - 1)
    return sampleVar
}

sampleVar = mr(ds, varMap, +, varFinal)


3.5 Caché de fuente de datos

La fuente de datos puede tener 0, 1 o varias posiciones. La fuente de datos con la posición 0 es la fuente de datos local. En el caso de varias ubicaciones, estas ubicaciones son copias de seguridad mutuas. El sistema seleccionará al azar una ubicación para realizar la computación distribuida. Cuando se le indique a la fuente de datos que guarde en caché el objeto de datos, el sistema seleccionará la ubicación donde recuperamos con éxito los datos la última vez.

El usuario puede indicar al sistema que almacene en caché la fuente de datos o que borre la caché. Para los algoritmos informáticos iterativos (como los algoritmos de aprendizaje automático), el almacenamiento en caché de datos puede mejorar en gran medida el rendimiento informático. Cuando la memoria del sistema es insuficiente, los datos almacenados en caché se borrarán. Si esto sucede, el sistema puede recuperar los datos porque la fuente de datos contiene todas las meta descripciones y funciones de conversión de datos.

Las funciones relacionadas con el almacenamiento en caché de la fuente de datos son:

  • cacheDS!: Indica al sistema que almacene en caché la fuente de datos
  • clearcacheDS!: Indica al sistema que borre la memoria caché después de la siguiente ejecución de la fuente de datos
  • cacheDSNow: Ejecute y almacene en caché la fuente de datos inmediatamente y devuelva el número total de líneas de caché
  • clearCacheDSNow: Borre la fuente de datos y la caché inmediatamente


3.6 Conversión de fuente de datos

Un objeto de fuente de datos también puede contener múltiples funciones de conversión de datos para procesar aún más los datos recuperados. El sistema ejecutará estas funciones de conversión de datos a su vez, con la salida de una función como entrada (y la única entrada) de la siguiente función.

Incluir la función de conversión de datos en la fuente de datos suele ser más eficaz que convertir la fuente de datos en la operación de computación central (es decir, la función de mapa). Si los datos recuperados solo necesitan calcularse una vez, no hay diferencia de rendimiento, pero causará una gran diferencia en el cálculo iterativo de fuentes de datos con objetos de datos almacenados en caché. Si la operación de conversión está en la operación de computación central, la conversión debe realizarse para cada iteración; si la operación de conversión está en la fuente de datos, solo se ejecutan una vez. La función transDS! Proporciona la función de convertir fuentes de datos.

Por ejemplo, antes de ejecutar la función iterativa de aprendizaje automático randomForestRegressor, los usuarios pueden necesitar completar manualmente los valores faltantes en los datos (por supuesto, el algoritmo de bosque aleatorio de DolphinDB tiene un procesamiento de valores faltantes incorporado). En este punto, puede usar transDS! Para procesar la fuente de datos de la siguiente manera: Para cada columna de características, use el valor promedio de la columna para completar los valores faltantes. Suponiendo que las columnas x0, x1, x2, x3 en la tabla son variables independientes y la columna y es la variable dependiente, el siguiente es el método de implementación:

ds = sqlDS(<select x0, x1, x2, x3, y from t>)
ds.transDS!(def (mutable t) {
    update t set x0 = nullFill(x0, avg(x0)), x1 = nullFill(x1, avg(x1)), x2 = nullFill(x2, avg(x2)), x3 = nullFill(x3, avg(x3))
    return t
})

randomForestRegressor(ds, `y, `x0`x1`x2`x3)

Otro ejemplo de conversión de una fuente de datos es la implementación del script de regresión logística mencionada en la sección 2.2. En la implementación de la sección 2.2, la llamada a la función de mapa incluye la operación de buscar la columna correspondiente de la tabla de la fuente de datos y convertirla en una matriz, lo que significa que estas operaciones ocurrirán en cada iteración. De hecho, cada iteración usará la misma matriz de entrada, este paso de conversión solo necesita ser llamado una vez. Por lo tanto, puede usar transDS! Para convertir la fuente de datos en un triple que contenga matrices x, xt e y:

def myLrTrans(t, yColName, xColNames, intercept) {
    if (intercept)
        x = matrix(t[xColNames], take(1.0, t.rows()))
    else
        x = matrix(t[xColNames])
    xt = x.transpose()
    y = t[yColName]
    return [x, xt, y]
}

def myLrMap(input, lastFinal) {
    x, xt, y = input
    placeholder, placeholder, theta = lastFinal
    // 之后的计算和2.2节相同
}

// myLrFinal和myLrTerm函数和2.2节相同

def myLr(mutable ds, yColName, xColNames, intercept, initTheta, tol) {
    ds.transDS!(myLrTrans{, yColName, xColNames, intercept})
    logLik, grad, theta = imr(ds, [0, 0, initTheta], myLrMap, +, myLrFinal, myLrTerm{, , tol})
    return theta
}

Supongo que te gusta

Origin blog.csdn.net/qq_41996852/article/details/112599763
Recomendado
Clasificación