2025, Dec 01 09:03
numpy.sum и torch.Tensor: почему возникает ошибка и что делать
Почему numpy.sum падает на torch.Tensor: различия API с NumPy/pandas, роль аргумента out и решения — torch.sum или преобразование тензора в массив NumPy.
Смешивать NumPy, pandas и PyTorch кажется удобным, пока внезапно не перестаёт быть таковым. Типичный пример: вызов numpy.sum для объекта torch.Tensor. Та же функция без проблем работает с массивом NumPy или pandas DataFrame, но для тензора PyTorch она приводит к ошибке. Понимание причин помогает избежать скрытых ловушек совместимости и сохранять предсказуемость численного кода.
Воспроизводим несоответствие
Поведение проявляется сразу при выполнении редукции по axis=1 для всех трёх типов данных.
data_src = [[1.1, 2.2], [1.1, 2.2], [1.1, 2.2]]
arr_np = np.array(data_src)
frame_pd = pd.DataFrame(data_src)
block_torch = torch.tensor(data_src)
np.sum(arr_np, axis=1) # работает
np.sum(frame_pd, axis=1) # работает
np.sum(block_torch, axis=1) # вызывает ошибку
Что на самом деле происходит
Это не баг. NumPy и PyTorch — разные библиотеки. Они стараются взаимодействовать, но не гарантируют полной совместимости API во всех операциях. На практике надёжнее всего не смешивать разные типы массивов внутри одного и того же вызова.
Ключ кроется в реализации редукций. В обеих библиотеках они доступны и как «чистые» функции, и как методы объектов. В NumPy и функциональная форма, и метод массива ndarray поддерживают аргумент out=. В PyTorch out= поддерживает только функциональная форма; метод — нет. Механизм редукций NumPy может делегировать вызов методу объекта и передать out= (включая вариант out=None). Для массивов NumPy это нормально, но конфликтует с torch.Tensor.sum: метод тензора параметр out= не принимает, поэтому возникает TypeError.
Коротко иллюстрируя разницу, в PyTorch поддерживается следующее:
torch.sum(block_torch, 1, out=None) # функциональная форма принимает 'out'
а метод с аргументом out — нет:
block_torch.sum(1, out=None) # метод не принимает 'out'
Путь редукции в NumPy может вызвать метод и передать out=, что и приводит к ошибке при вводе типа torch.Tensor.
Решение: не смешивайте типы массивов в редукции
Простой способ избежать ошибки — сохранять единый тип массива. Если вам нужна numpy.sum, предварительно преобразуйте тензор в массив NumPy.
data_src = [[1.1, 2.2], [1.1, 2.2], [1.1, 2.2]]
arr_np = np.array(data_src)
frame_pd = pd.DataFrame(data_src)
block_torch = torch.tensor(data_src)
np.sum(arr_np, axis=1) # работает
np.sum(frame_pd, axis=1) # работает
np.sum(block_torch.numpy(), axis=1) # работает после преобразования в NumPy
Так вызов остаётся в «домене» NumPy, соответствуя его ожиданиям и обработке аргументов.
Почему это важно
Совместимость — это полезно, но различия в API реальны и порой тонкие. Опора на вызовы «через библиотеку» может неожиданно ломаться, особенно в нюансах вроде out=. В документации NumPy указано, что numpy.sum принимает «array-like» вход; считать ли torch.Tensor «array-like» в данном контексте — не гарантируется. Без явного обещания поддерживать torch.Tensor такое поведение не является багом. Для долгосрочной переносимости между библиотеками массивов ведётся работа над стандартом Python Array API. Пакет array-api-compat может помочь, если вам нужен интерфейс, следующий этому стандарту.
Выводы
Если вам нужно поведение NumPy, держите данные в массиве NumPy перед вызовом функций NumPy. То же и для PyTorch: для тензоров используйте torch.sum. Несоответствие здесь связано с тем, что NumPy передаёт out= методовой форме, которую тензор PyTorch не принимает. В текущем состоянии межбиблиотечной совместимости это ожидаемо. Если проекту требуется более гладкая работа «на стыке», выбирайте единый тип массива на операцию или присмотритесь к экосистеме Python Array API.