Как вы индексируете RaggedTensor вдоль неровного измерения в TensorFlow?

#python #tensorflow #ragged

#python #tensorflow #неровный

Вопрос:

Мне нужно получить значения в неровном тензоре путем индексации по неровному измерению. Некоторое индексирование работает ( [:, :x] , [:, -x:] или [:, x:y] ), но не прямое индексирование ( [:, x] ):

 R = tf.RaggedTensor.from_tensor([[1, 2, 3], [4, 5, 6]])
print(R[:, :2]) # RaggedTensor([[1, 2], [4, 5]])
print(R[:, 1:2]) # RaggedTensor([[2], [5]])
print(R[:, 1])  # ValueError: Cannot index into an inner ragged dimension.
  

В документации объясняется, почему это не удается:

RaggedTensors поддерживает многомерную индексацию и нарезку с одним ограничением: индексация в неровном измерении запрещена. Этот случай проблематичен, потому что указанное значение может существовать в некоторых строках, но не в других. В таких случаях не очевидно, следует ли нам (1) повышать IndexError; (2) использовать значение по умолчанию; или (3) пропустить это значение и вернуть тензор с меньшим количеством строк, чем мы начали. Следуя руководящим принципам Python («Перед лицом двусмысленности откажитесь от соблазна угадать»), в настоящее время мы запрещаем эту операцию.

Это имеет смысл, но как мне на самом деле реализовать варианты 1, 2 и 3? Должен ли я преобразовать неровный массив в массив тензоров Python и вручную выполнить итерацию по ним? Есть ли более эффективное решение? Тот, который будет работать на 100% в графике TensorFlow, не проходя через интерпретатор Python?

Ответ №1:

Если у вас есть 2D RaggedTensor, то вы можете получить поведение (3) с помощью:

 def get_column_slice_v3(rt, column):
  assert column >= 0  # Negative column index not supported
  slice = rt[:, column:column 1]
  return slice.flat_values
  

И вы можете получить поведение (1), добавив утверждение, что rt.nrows() == tf.size(slice.flat_values):

 def get_column_slice_v1(rt, column):
  assert column >= 0  # Negative column index not supported
  slice = rt[:, column:column 1]
  with tf.assert_equal(rt.nrows(), tf.size(slice.flat_values):
    return tf.identity(slice.flat_values)
  

Чтобы получить поведение (2), я думаю, что самый простой способ, вероятно, объединить вектор значений по умолчанию, а затем снова выполнить срез:

 def get_colum_slice_v2(rt, column, default=None):
  assert column >= 0  # Negative column index not supported
  slice = rt[:, column:column 1]
  if default is None:
    defaults = tf.zeros([slice.nrows(), 1], slice.dtype)
  ele:
    defaults = tf.fill([slice.nrows(), 1], default)
  slice_plus_default = tf.concat([rt, defaults], axis=1)
  slice2 = slice_plus_defaults[:1]
  return slice2.flat_values
  

Их можно расширить для поддержки тензоров с неровностями более высокой размерности, но логика становится немного сложнее. Также должна быть возможность расширить их для поддержки отрицательных индексов столбцов.