Вызов .ToArray для Enumerable повреждает Enumerable - PullRequest
3 голосов
/ 28 марта 2020

Я не уверен, указывается ли это c для ML. NET, но это происходит в контексте этого.

Я использую ML. NET для классификации некоторых изображений , Я понял, что это представляет серьезную разницу, независимо от того, назову я .ToArray() на полученном IEnumerable или нет. Первое приводит к тому, что все элементы массива становятся идентичными последним.

IEnumerable<ImageData> dataCollection = imagePaths.Select(path => new ImageData(path));
IDataView targetDataView = _mlContext.Data.LoadFromEnumerable(dataCollection);
IDataView predictionView = _transformerModel.Transform(targetDataView); 
return _mlContext.Data.CreateEnumerable<ImagePrediction>(predictionView, true).ToArray();

В показанном выше примере все получающиеся предсказания будут иметь путь к изображению, установленный на последний путь изображения в imagePaths.

Я не верю, что это предполагаемое поведение. Что вызывает это и как я могу безопасно предотвратить это? На данный момент я решил просто не звонить .ToArray(), но я хотел бы узнать больше об этой проблеме.

1 Ответ

2 голосов
/ 28 марта 2020

Кажется, проблема в механизме прогнозирования, где ограничивается использование памяти, row используется повторно согласно reuseRowObject. Поэтому, когда вызывается метод ToList() или ToArray(), для проецирования списка / массива используется только последний элемент.

public IEnumerable<TDst> RunPipe(bool reuseRowObject)
{
    var curCounter = _counter;
    using (var cursor = _cursorablePipe.GetCursor())
    {
        TDst row = null;
        while (cursor.MoveNext())
        {
            if (!reuseRowObject || row == null)
                row = new TDst();

            cursor.FillValues(row);
            yield return row;
            if (curCounter != _counter)
                throw Contracts.Except("An attempt was made to keep iterating after the pipe has been reset.");
        }
    }
}

Вызывающая сторона - CreateEnumerable(), где для reuseRowObject явно установлено значение true.

public IEnumerable<TRow> CreateEnumerable<TRow>(IDataView data, bool reuseRowObject,
    bool ignoreMissingColumns = false, SchemaDefinition schemaDefinition = null)
    where TRow : class, new()
{
    _env.CheckValue(data, nameof(data));
    _env.CheckValueOrNull(schemaDefinition);

    var engine = new PipeEngine<TRow>(_env, data, ignoreMissingColumns, schemaDefinition);
    return engine.RunPipe(reuseRowObject);
}

Установка reuseRowObject на false должна решить вашу проблему.

...