Swift中使用Accelerate实现二维数组与向量逐元素相乘的问题
如何用Swift Accelerate实现NumPy风格的二维数组与向量逐元素相乘?
NumPy示例(预期行为)
以下NumPy代码实现了二维数组与列向量的广播逐元素相乘:
import numpy as np a = np.array([[3, 5, 8, 7, 2], [1, 4, 9, 1, 3], [2, 7, 3, 1, 2], [5, 4, 9, 1, 6], [4, 2, 9, 6, 7]]) b = np.array([[2], [0.5], [9], [1], [18]]) c = a * b print(c)
输出结果:
[[ 6. 10. 16. 14. 4. ] [ 0.5 2. 4.5 0.5 1.5] [ 18. 63. 27. 9. 18. ] [ 5. 4. 9. 1. 6. ] [ 72. 36. 162. 108. 126. ]]
原Swift代码的问题
你尝试用Accelerate实现,但输出结果错误:
import Foundation import Accelerate let a: [Float] = [3, 5, 8, 7, 2, 1, 4, 9, 1, 3, 2, 7, 3, 1, 2, 5, 4, 9, 1, 6, 4, 2, 9, 6, 7] let b: [Float] = [2, 0.5, 9, 1, 18] var c: [Float] = Array(repeating: 0, count: 25) vDSP_vmul(a, 1, b, 1, &c, 1, 25) print(c)
错误输出:
[6.0, 2.5, 72.0, 7.0, 36.0, 0.0, 0.0, 0.0, 1.104904e-39, 4.7331654e-30, 3.7740115e-24, 6.737052e-37, 1.806498e-35, 1.19422e-39, 5.1722454e-26, 5.9787867e-25, 2.7286e-41, 0.0, 0.0, 0.0, 1.6543612e-24, 0.0, 0.0, 5.533857e-39, 3.73e-43]
问题根源:vDSP_vmul是等长向量逐元素相乘,但你的场景需要NumPy的广播机制——将列向量b的每个元素对应到a的整行,再逐元素相乘。直接用vDSP_vmul会因为b长度远小于a而越界访问内存,导致后续结果为垃圾值。
正确的Accelerate实现
方法1:逐行执行标量乘法(高效推荐)
使用vDSP_vsmul函数,它可以将一个向量的所有元素乘以同一个标量。遍历a的每一行,用b中对应的元素作为标量完成乘法:
import Foundation import Accelerate let a: [Float] = [3, 5, 8, 7, 2, 1, 4, 9, 1, 3, 2, 7, 3, 1, 2, 5, 4, 9, 1, 6, 4, 2, 9, 6, 7] let b: [Float] = [2, 0.5, 9, 1, 18] let rowCount = 5 let columnCount = 5 var c: [Float] = Array(repeating: 0, count: rowCount * columnCount) for i in 0..<rowCount { let rowStart = i * columnCount let rowRange = rowStart..<rowStart + columnCount let scalar = b[i] // 对a的当前行执行标量乘法,结果存入c的对应位置 vDSP_vsmul(a[rowRange], 1, &scalar, &c[rowRange], 1, vDSP_Length(columnCount)) } print(c)
输出结果与NumPy一致:
[6.0, 10.0, 16.0, 14.0, 4.0, 0.5, 2.0, 4.5, 0.5, 1.5, 18.0, 63.0, 27.0, 9.0, 18.0, 5.0, 4.0, 9.0, 1.0, 6.0, 72.0, 36.0, 162.0, 108.0, 126.0]
方法2:先扩展向量再相乘(直观但内存占用高)
先将b扩展为和a等长的扁平化数组(每行重复b[i]对应次数),再用vDSP_vmul完成逐元素相乘:
import Foundation import Accelerate let a: [Float] = [3, 5, 8, 7, 2, 1, 4, 9, 1, 3, 2, 7, 3, 1, 2, 5, 4, 9, 1, 6, 4, 2, 9, 6, 7] let b: [Float] = [2, 0.5, 9, 1, 18] let rowCount = 5 let columnCount = 5 var c: [Float] = Array(repeating: 0, count: rowCount * columnCount) // 扩展b为与a等长的数组:每行重复b[i] 5次 var expandedB: [Float] = [] for scalar in b { expandedB.append(contentsOf: Array(repeating: scalar, count: columnCount)) } // 执行逐元素相乘 vDSP_vmul(a, 1, expandedB, 1, &c, 1, vDSP_Length(rowCount * columnCount)) print(c)
这种方法需要额外内存存储扩展后的b,适合小数据量场景,大数据量下推荐方法1。
内容的提问来源于stack exchange,提问作者wigging
相关产品推荐
相关产品推荐

