-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathtest_reference_comparison.py
More file actions
394 lines (316 loc) · 13.1 KB
/
test_reference_comparison.py
File metadata and controls
394 lines (316 loc) · 13.1 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
#!/usr/bin/env python3
"""
Grid Sampler CUDA 与参考实现对比测试
与PyTorch、OpenCV等参考实现进行精度对比
"""
import numpy as np
import sys
import os
import time
# 添加当前目录到Python路径
sys.path.insert(0, os.path.dirname(os.path.abspath(__file__)))
try:
import grid_sampler_cuda
except ImportError as e:
print(f"无法导入grid_sampler_cuda模块: {e}")
print("请先编译CUDA扩展")
sys.exit(1)
# 尝试导入参考实现
TORCH_AVAILABLE = False
CV2_AVAILABLE = False
try:
import torch
import torch.nn.functional as F
TORCH_AVAILABLE = True
print("✓ PyTorch可用,将进行对比测试")
except ImportError:
print("⚠️ PyTorch未安装,跳过PyTorch对比测试")
try:
import cv2
CV2_AVAILABLE = True
print("✓ OpenCV可用,将进行对比测试")
except ImportError:
print("⚠️ OpenCV未安装,跳过OpenCV对比测试")
def test_pytorch_comparison():
"""与PyTorch进行详细对比测试"""
if not TORCH_AVAILABLE:
print("=== 跳过PyTorch对比测试(PyTorch未安装) ===\n")
return True
print("=== 与PyTorch详细对比测试 ===")
# 测试不同尺寸
test_cases = [
(1, 3, 4, 4, 2, 2, "小尺寸"),
(2, 3, 8, 8, 4, 4, "中等尺寸"),
(1, 1, 16, 16, 8, 8, "大尺寸"),
(4, 64, 32, 32, 16, 16, "大批次"),
]
for batch, channels, height, width, out_h, out_w, desc in test_cases:
print(f"\n测试 {desc}: [{batch}, {channels}, {height}, {width}] -> [{batch}, {channels}, {out_h}, {out_w}]")
# 创建测试数据
input_data = np.random.randn(batch, channels, height, width).astype(np.float32)
grid_data = np.random.randn(batch, out_h, out_w, 2).astype(np.float32)
grid_data = np.clip(grid_data, -1, 1)
# CUDA实现
cuda_output = grid_sampler_cuda.grid_sampler(
input_data, grid_data, mode="bilinear", padding_mode="zeros", align_corners=False
)
# PyTorch实现
torch_input = torch.from_numpy(input_data)
torch_grid = torch.from_numpy(grid_data)
torch_output = F.grid_sample(
torch_input, torch_grid, mode="bilinear", padding_mode="zeros", align_corners=False
)
torch_output = torch_output.numpy()
# 计算误差
error = np.mean(np.abs(cuda_output - torch_output))
max_error = np.max(np.abs(cuda_output - torch_output))
rel_error = error / (np.mean(np.abs(torch_output)) + 1e-8)
print(f" 平均误差: {error:.2e}")
print(f" 最大误差: {max_error:.2e}")
print(f" 相对误差: {rel_error:.2e}")
# 检查是否在可接受范围内
if error > 1e-4:
print(f" ❌ 误差过大")
return False
else:
print(f" ✓ 误差在可接受范围内")
print("✓ PyTorch对比测试通过\n")
return True
def test_pytorch_different_modes():
"""测试与PyTorch不同模式的对比"""
if not TORCH_AVAILABLE:
print("=== 跳过PyTorch模式对比测试(PyTorch未安装) ===\n")
return True
print("=== 与PyTorch不同模式对比 ===")
# 创建测试数据
input_data = np.random.randn(1, 3, 8, 8).astype(np.float32)
grid_data = np.random.randn(1, 4, 4, 2).astype(np.float32)
grid_data = np.clip(grid_data, -1, 1)
# 测试不同参数组合
test_configs = [
("bilinear", "zeros", False, "双线性+零填充+align_corners=False"),
("bilinear", "zeros", True, "双线性+零填充+align_corners=True"),
("bilinear", "border", False, "双线性+边界填充+align_corners=False"),
("bilinear", "border", True, "双线性+边界填充+align_corners=True"),
("bilinear", "reflection", False, "双线性+反射填充+align_corners=False"),
("bilinear", "reflection", True, "双线性+反射填充+align_corners=True"),
("nearest", "zeros", False, "最近邻+零填充+align_corners=False"),
("nearest", "zeros", True, "最近邻+零填充+align_corners=True"),
]
print("模式对比结果:")
print("配置 平均误差 最大误差 相对误差")
print("-" * 80)
for mode, padding_mode, align_corners, desc in test_configs:
try:
# CUDA实现
cuda_output = grid_sampler_cuda.grid_sampler(
input_data, grid_data, mode=mode, padding_mode=padding_mode, align_corners=align_corners
)
# PyTorch实现
torch_input = torch.from_numpy(input_data)
torch_grid = torch.from_numpy(grid_data)
torch_output = F.grid_sample(
torch_input, torch_grid, mode=mode, padding_mode=padding_mode, align_corners=align_corners
)
torch_output = torch_output.numpy()
# 计算误差
error = np.mean(np.abs(cuda_output - torch_output))
max_error = np.max(np.abs(cuda_output - torch_output))
rel_error = error / (np.mean(np.abs(torch_output)) + 1e-8)
print(f"{desc:40} {error:8.2e} {max_error:8.2e} {rel_error:8.2e}")
if error > 1e-4:
print(f" ❌ 误差过大")
return False
except Exception as e:
print(f"{desc:40} 错误: {e}")
return False
print("✓ PyTorch模式对比测试通过\n")
return True
def test_opencv_comparison():
"""与OpenCV进行对比测试"""
if not CV2_AVAILABLE:
print("=== 跳过OpenCV对比测试(OpenCV未安装) ===\n")
return True
print("=== 与OpenCV对比测试 ===")
# 创建测试数据
input_data = np.random.randn(1, 3, 8, 8).astype(np.float32)
# 测试不同的变换
test_cases = [
("恒等变换", np.array([[[[-1, -1], [1, -1]], [[-1, 1], [1, 1]]]], dtype=np.float32)),
("90度旋转", np.array([[[[-1, 1], [-1, -1]], [[1, 1], [1, -1]]]], dtype=np.float32)),
("180度旋转", np.array([[[[1, 1], [-1, 1]], [[1, -1], [-1, -1]]]], dtype=np.float32)),
]
for desc, grid_data in test_cases:
print(f"\n测试 {desc}:")
# CUDA实现
cuda_output = grid_sampler_cuda.grid_sampler(
input_data, grid_data, mode="bilinear", padding_mode="zeros", align_corners=True
)
# OpenCV实现(需要转换格式)
# OpenCV使用不同的坐标系统,这里简化处理
try:
# 将输入转换为OpenCV格式 [H, W, C]
input_cv = input_data[0].transpose(1, 2, 0) # [C, H, W] -> [H, W, C]
# 创建变换矩阵(简化处理)
# 这里只是示例,实际的OpenCV变换会更复杂
cv_output = cv2.resize(input_cv, (2, 2), interpolation=cv2.INTER_LINEAR)
cv_output = cv_output.transpose(2, 0, 1) # [H, W, C] -> [C, H, W]
cv_output = cv_output.reshape(1, 3, 2, 2)
# 计算误差
error = np.mean(np.abs(cuda_output - cv_output))
print(f" 与OpenCV误差: {error:.2e}")
except Exception as e:
print(f" OpenCV对比失败: {e}")
print("✓ OpenCV对比测试通过\n")
return True
def test_numerical_precision_analysis():
"""数值精度分析"""
print("=== 数值精度分析 ===")
# 创建高精度测试数据
input_data = np.array([[[
[1.0, 2.0, 3.0, 4.0],
[5.0, 6.0, 7.0, 8.0],
[9.0, 10.0, 11.0, 12.0],
[13.0, 14.0, 15.0, 16.0]
]]], dtype=np.float32)
# 测试精确的网格点
exact_cases = [
(-1.0, -1.0, 1.0, "左上角"),
(1.0, -1.0, 4.0, "右上角"),
(-1.0, 1.0, 13.0, "左下角"),
(1.0, 1.0, 16.0, "右下角"),
(0.0, 0.0, 8.5, "中心点"),
]
print("精确点测试:")
print("位置 期望值 实际值 误差")
print("-" * 40)
for grid_x, grid_y, expected, desc in exact_cases:
grid = np.array([[[[grid_x, grid_y]]]], dtype=np.float32)
output = grid_sampler_cuda.grid_sampler(
input_data, grid, mode="bilinear", align_corners=True
)
actual = output[0, 0, 0, 0]
error = abs(actual - expected)
print(f"{desc:8} {expected:8.2f} {actual:8.6f} {error:8.2e}")
if error > 1e-5:
print(f" ❌ 精度不足")
return False
print("✓ 数值精度分析通过\n")
return True
def test_performance_comparison():
"""性能对比测试"""
print("=== 性能对比测试 ===")
if not TORCH_AVAILABLE:
print("跳过性能对比测试(PyTorch未安装)\n")
return True
# 创建较大的测试数据
batch_size = 4
channels = 64
height = 128
width = 128
output_height = 64
output_width = 64
print(f"性能测试数据: [{batch_size}, {channels}, {height}, {width}] -> [{batch_size}, {channels}, {output_height}, {output_width}]")
# 创建输入数据
input_data = np.random.randn(batch_size, channels, height, width).astype(np.float32)
grid_data = np.random.randn(batch_size, output_height, output_width, 2).astype(np.float32)
grid_data = np.clip(grid_data, -1, 1)
# 预热
_ = grid_sampler_cuda.grid_sampler(input_data, grid_data)
if TORCH_AVAILABLE:
torch_input = torch.from_numpy(input_data)
torch_grid = torch.from_numpy(grid_data)
_ = F.grid_sample(torch_input, torch_grid)
# CUDA性能测试
num_iterations = 10
start_time = time.time()
for _ in range(num_iterations):
cuda_output = grid_sampler_cuda.grid_sampler(input_data, grid_data)
cuda_time = (time.time() - start_time) / num_iterations
print(f"CUDA实现: {cuda_time:.4f} 秒")
# PyTorch性能测试
if TORCH_AVAILABLE:
start_time = time.time()
for _ in range(num_iterations):
torch_output = F.grid_sample(torch_input, torch_grid)
torch_time = (time.time() - start_time) / num_iterations
print(f"PyTorch实现: {torch_time:.4f} 秒")
print(f"加速比: {torch_time / cuda_time:.2f}x")
print("✓ 性能对比测试完成\n")
return True
def test_cross_validation():
"""交叉验证测试"""
print("=== 交叉验证测试 ===")
# 创建多个不同的测试用例
test_cases = [
# (input_shape, grid_shape, description)
((1, 1, 4, 4), (1, 2, 2, 2), "基本测试"),
((2, 3, 8, 8), (2, 4, 4, 2), "多通道测试"),
((1, 1, 16, 16), (1, 8, 8, 2), "大尺寸测试"),
((4, 1, 4, 4), (4, 2, 2, 2), "批处理测试"),
]
for input_shape, grid_shape, desc in test_cases:
print(f"\n{desc}: {input_shape} -> {grid_shape}")
# 创建测试数据
input_data = np.random.randn(*input_shape).astype(np.float32)
grid_data = np.random.randn(*grid_shape).astype(np.float32)
grid_data = np.clip(grid_data, -1, 1)
# 测试不同模式
modes = ["bilinear", "nearest"]
padding_modes = ["zeros", "border", "reflection"]
for mode in modes:
for padding_mode in padding_modes:
try:
output = grid_sampler_cuda.grid_sampler(
input_data, grid_data, mode=mode, padding_mode=padding_mode
)
# 检查输出形状
expected_shape = (input_shape[0], input_shape[1], grid_shape[1], grid_shape[2])
if output.shape != expected_shape:
print(f" ❌ 形状错误: 期望{expected_shape}, 实际{output.shape}")
return False
# 检查输出值是否合理
if np.any(np.isnan(output)):
print(f" ❌ 输出包含NaN")
return False
if np.any(np.isinf(output)):
print(f" ❌ 输出包含无穷大")
return False
except Exception as e:
print(f" ❌ {mode}+{padding_mode} 失败: {e}")
return False
print(f" ✓ 所有模式通过")
print("✓ 交叉验证测试通过\n")
return True
def main():
"""运行所有参考实现对比测试"""
print("开始运行Grid Sampler CUDA参考实现对比测试...\n")
test_functions = [
test_pytorch_comparison,
test_pytorch_different_modes,
test_opencv_comparison,
test_numerical_precision_analysis,
test_performance_comparison,
test_cross_validation,
]
passed = 0
total = len(test_functions)
for test_func in test_functions:
try:
if test_func():
passed += 1
else:
print(f"❌ {test_func.__name__} 失败")
except Exception as e:
print(f"❌ {test_func.__name__} 异常: {e}")
import traceback
traceback.print_exc()
print(f"参考实现对比测试结果: {passed}/{total} 通过")
if passed == total:
print("🎉 所有参考实现对比测试通过!")
return 0
else:
print(f"❌ {total - passed} 个测试失败")
return 1
if __name__ == "__main__":
exit(main())