tensorflow py_func es útil pero hace que mi entrenamiento sea muy lento.

Tengo algún problema de eficiencia al usar la función tensorflow py_func.

Contexto

En mi proyecto, tengo un lote de tensor input_features de tamaño [? max_items m] [? max_items m] . La primera dimensión se establece en ? porque es una forma dinámica (el lote se lee para un lector de flujo de tensión personalizado y se baraja usando tf.train.shuffle_batch_join ()). La segunda dimensión corresponde a un límite superior (el número máximo de elementos que puedo tomar para mi ejemplo), la tercera dimensión corresponde al espacio de dimensión de la característica. También tengo un tensor num_items que tiene dimensión de tamaño de lote (por lo que la forma es (?,) ), num_items indica el número de elementos en el ejemplo, otros están configurados en 0 (en un estilo de escritura input_feature[k, num_items[k]:, :] = 0 )

Problema

Mi flujo de trabajo necesita algunas operaciones personalizadas de Python (especialmente para tratar con la indexación, necesito o una instancia para realizar operaciones de agrupamiento en clústeres en algunos ejemplos) y uso algunas funciones numpy envueltas en la función py_func . Esto funciona bien, pero el entrenamiento se vuelve muy lento (alrededor de 50 veces más lento que un modelo sin este py_func), y la función en sí no consume mucho tiempo.

Preguntas

1 – ¿Es normal el aumento de este tiempo de computación? La función envuelta en py_func me da un nuevo tensor que se multiplica aún más en el proceso. ¿Explica el tiempo de computación? (Me refiero a que el degradado puede ser más difícil de calcular con esa función).

2 – Estoy tratando de modificar mi procesamiento y evito usar la función py_func . Sin embargo, fue muy útil para extraer datos con indexación numpy (especialmente con mi formato de datos), y tengo algunas dificultades para pasarlos de una manera TF. Por ejemplo, si tengo un tensor t1 con forma [-1, n_max, m] (la primera dimensión es batch_size, que es dinámica) y t2 con forma [-1,2] contiene números enteros. ¿Existe una manera fácil de realizar una operación media en tensorflow que resulte en t_mean_chunk con forma (-1, m) donde (en una formulación numpy): t_mean_chunk[i,:] = np.mean(t1[i, t2[i,0]:t2[i,1], :], axis=0) ? Este fue (entre otras operaciones) el tipo de cosas que estaba haciendo en función envuelta.

La pregunta 1 es difícil de responder sin el py_func exacto, pero como hpaulj mencionó en su comentario, no es demasiado sorprendente que esté ralentizando las cosas. Como una alternativa de caso más tf.scan , tf.scan o tf.while_loop con un TensorArray puede ser algo más rápido. Sin embargo, el mejor caso es tener una solución vectorizada con TensorFlow ops, que creo que es posible en este caso.

En cuanto a la pregunta 2, no estoy seguro de que cuente como fácil, pero aquí hay una función que calcula su expresión de indexación:

 import tensorflow as tf def range_mean(index_ranges, values): """Take the mean of `values` along ranges specified by `index_ranges`. return[i, ...] = tf.reduce_mean( values[i, index_ranges[i, 0]:index_ranges[i, 1], ...], axis=0) Args: index_ranges: An integer Tensor with shape [N x 2] values: A Tensor with shape [N x M x ...]. Returns: A Tensor with shape [N x ...] containing the means of `values` having indices in the ranges specified. """ m_indices = tf.range(tf.shape(values)[1])[None] # Determine which parts of `values` will be in the result selected = tf.logical_and(tf.greater_equal(m_indices, index_ranges[:, :1]), tf.less(m_indices, index_ranges[:, 1:])) n_indices = tf.tile(tf.range(tf.shape(values)[0])[..., None], [1, tf.shape(values)[1]]) segments = tf.where(selected, n_indices + 1, tf.zeros_like(n_indices)) # Throw out segment 0, since that's our "not included" segment segment_sums = tf.unsorted_segment_sum( data=values, segment_ids=segments, num_segments=tf.shape(values)[0] + 1)[1:] divisor = tf.cast(index_ranges[:, 1] - index_ranges[:, 0], dtype=values.dtype) # Pad the shape of `divisor` so that it broadcasts against `segment_sums`. divisor_shape_padded = tf.reshape( divisor, tf.concat([tf.shape(divisor), tf.ones([tf.rank(values) - 2], dtype=tf.int32)], axis=0)) return segment_sums / divisor_shape_padded 

Ejemplo de uso:

 index_range_tensor = tf.constant([[2, 4], [1, 6], [0, 3], [0, 9]]) values_tensor = tf.reshape(tf.range(4 * 10 * 5, dtype=tf.float32), [4, 10, 5]) with tf.Session(): tf_result = range_mean(index_range_tensor, values_tensor).eval() index_range_np = index_range_tensor.eval() values_np = values_tensor.eval() for i in range(values_np.shape[0]): print("Slice {}: ".format(i), tf_result[i], numpy.mean(values_np[i, index_range_np[i, 0]:index_range_np[i, 1], :], axis=0)) 

Huellas dactilares:

 Slice 0: [ 12.5 13.5 14.5 15.5 16.5] [ 12.5 13.5 14.5 15.5 16.5] Slice 1: [ 65. 66. 67. 68. 69.] [ 65. 66. 67. 68. 69.] Slice 2: [ 105. 106. 107. 108. 109.] [ 105. 106. 107. 108. 109.] Slice 3: [ 170. 171. 172. 173. 174.] [ 170. 171. 172. 173. 174.]