Вопрос или проблема
Я попытался написать код для вычисления массива Numpy с использованием C, создал его в виде lib.so и попробовал вычислить большую матрицу. Затем я попытался произвести вычисления с помощью Numpy в Python. Оказалось, что при сравнении значений они не совпадали.
import ctypes
from ctypes import CDLL
from ctypes import c_size_t
import numpy as np
import time
# загружаем библиотеку
mylib = CDLL("example.so")
# C-тип, соответствующий numpy array для 3D массивов и 1D массива
ND_POINTER_3 = np.ctypeslib.ndpointer(dtype=np.float32, ndim=3, flags="C_CONTIGUOUS")
ND_POINTER_1 = np.ctypeslib.ndpointer(dtype=np.float32, ndim=1, flags="C_CONTIGUOUS")
# определяем прототипы
mylib.add_vector.argtypes = [ND_POINTER_3, ND_POINTER_3, ND_POINTER_1, ND_POINTER_3, c_size_t]
mylib.add_vector.restype = ctypes.c_void_p
# Инициализируем numpy массивы (90x160x3 для la и lb, 90x160x1 для gm, и 90x160x3 для out)
np.random.seed(0)
la = np.random.rand(90, 160, 3).astype(np.float32)
lb = np.random.rand(90, 160, 3).astype(np.float32)
gm = np.random.rand(90, 160, 1).astype(np.float32)
out = np.zeros((90, 160, 3), dtype=np.float32)
# Уплощаем массив gm для передачи в виде 1D массива
gm_flat = gm.flatten()
s = time.perf_counter()
for i in range(1000):
mylib.add_vector(la, lb, gm_flat, out, la.size)
out = np.array(out)
e = time.perf_counter()
print(f"Время C : {e-s}")
s = time.perf_counter()
for i in range(1000):
test_result = la * lb + la * (1.0 - la)
e = time.perf_counter()
print(f"Время Python : {e-s}")
print(out[0, 1, :])
print(test_result[0, 1, :])
А вот и мой код на C
#include <stddef.h>
// Функция сложения векторов, поддерживающая 3D массивы и 1D массивы
void add_vector(float *la, float *lb, float *gm, float *out, size_t size) {
for (size_t i = 0; i < size; ++i) {
// Выполняем поэлементную операцию: ls = la * gm + lb * (1.0 - gm)
size_t gm_index = (i / 3);
out[i] = la[i] * gm[gm_index] + lb[i] * (1.0f - gm[gm_index]);
}
}
Компиляция
gcc -shared -o example.so -fPIC module_vector.c
Я надеюсь получить знания и найти правильное решение. Вот пример моего результата.
Время C : 0.05158950947225094
Время Python : 0.05883005540817976
[0.6418773 0.2731492 0.50642085]
[0.6623829 0.2818951 0.4456129]
Ответ или решение
Разница между результатами, полученными с использованием NumPy и C, может быть вызвана несколькими причинами. Давайте рассмотрим это более подробно и проанализируем, как можно исправить ситуацию.
Причины несовпадения значений:
-
Проблемы с индексированием:
В вашей реализации C функцияadd_vector
использует одномерный индекс для доступа к элементам трёхмерного массиваla
иlb
. В частности, переменнаяgm_index
рассчитывается какi / 3
, что подразумевает, что вы ожидаете, что размер первого измерения равен 3. Это может привести к путанице с индексами, если ваши данные имеют другой размер.Ваша функция должна учитывать, что каждый элемент
gm
используется для расчета нескольких элементов. Даже если вgm
только одно значение на плавающей запятой, вам нужно убедиться, что вы правильно обращаетесь к каждому элементуla
иlb
. -
Порядок вычислений:
Результаты, полученные с помощью NumPy, могут слегка отличаться от результатов C из-за порядка операций с плавающей запятой. Это происходит из-за особенностей арифметических операций с числами с плавающей запятой, которые могут давать разные результаты в зависимости от порядка вычислений. -
Тип данных и округление:
Убедитесь, что используется один и тот же тип данных (в данном случаеfloat32
), как в C, так и в Python. Кроме того, округление ошибок может привести к различиям в конечных значениях.
Решение проблемы:
-
Исправьте индексирование в функции C. Вам нужно передавать правильный индекс в
gm
для каждого элемента:void add_vector(float *la, float *lb, float *gm, float *out, size_t size) { for (size_t i = 0; i < size; ++i) { size_t gm_index = (i / (size / 3)); // Измените это согласно размерности gm out[i] = la[i] * gm[gm_index] + lb[i] * (1.0f - gm[gm_index]); } }
-
Проверьте реализацию и поправьте порядок вычислений: Убедитесь, что как в C, так и в Python относительно порядка вы вычисляете значения. Вычисления могут давать разные результаты при отсутствии строгой последовательности.
-
Тестирование и отладка: Проверьте промежуточные результаты в обеих реализациях, чтобы понять, на каком этапе происходит расхождение. Добавьте дополнительные выводы для диагностики.
Заключение:
Проблема с различием в значениях, полученных из C и NumPy, часто сводится к вопросам индексирования и порядка операций с плавающей запятой. Настоятельно рекомендуется внимательно просмотреть код и протестировать его на небольших наборах данных, чтобы убедиться, что все выполняется правильно до перехода к более крупным массивам.
С такими изменениями вы сможете минимизировать разницу между результатами.