【发布时间】:2014-03-06 06:43:15
【问题描述】:
我之前做过很多 OpenGL 和着色器,现在,我决定尝试一下 OpenCL。我看了一些在线教程,并开始阅读有关该主题的书籍。为了更好地理解,并且因为我相信最好的学习方式是明智地尝试并从这样做时出现的问题中学习,我决定开始为全连接感知器实现内核。
对于那些不知道那是什么的人,我将解释基本概念。它是一个神经网络,其中一层的每个神经元都连接到下一层的每个神经元。每个神经元只有一个动作要执行:执行上一层所有神经元的总和,每个神经元按不同的值加权。
这看起来很容易实现,在阅读了论文“Parallel Neural Network Training with OpenCL”后,我通过以下方式实现了它
每一层都依赖于前一层,它们由主机按顺序运行
为了计算一个层,我使用层内神经元数量的全局工作大小(可能非常大,例如数万个)运行我的内核。这使得所有神经元都相互独立地执行求和。
每个神经元(由其 global_work_id 标识)与前一层的所有神经元进行加权求和。
这是我功能齐全的 opencl 内核:
/**
* @brief Computes one layer of the perceptron given the previous one and the
* weights
* The kernel is run once for each layer.
* The work items are each tasked with computing the output of a single neuron
* of the out layer.
*
* @param out_layer_size
* Size of the output layer (number of elements in the output array that will
* contain the result for each neuron).
* @param in_layer_size
* Number of elements of the input layer
* @param in_value
* Values of the neuron in the previous layer
* @param in_weights
* Array containing the weights for each input neuron. It is organised as a
* two dimensional matrix, written by concatenating each line in the array
* [ w11, w12, w13, ...
* w21, w22, w23, ...
* ..., ..., ..., ...
* ]
* Where wij is the weight linking the neuron i of the input layer to the
* neuron j of the output layer
* @param out_values
* Computed values for the current layer
*/
void kernel perceptron(global const int* in_layer_size, global const int* out_layer_size, global const float *in_value, global const float* in_weights, global float* out_values)
{
private const int global_id = get_global_id(0);
private const int out_layer_s = *out_layer_size;
private const int in_layer_s = *in_layer_size;
private const int offset = out_layer_s * global_id;
private float sum = 0.;
for(int i=0; i < in_layer_s; i++) {
sum += in_weights[i*out_layer_s+global_id] * in_value[i];
}
//out_values[global_id] = sigma(sum);
out_values[global_id] = sum;
}
这是我调用它的方式:
queue.enqueueNDRangeKernel(kernel, cl::NullRange,cl::NDRange(number of neurons within layer),cl::NullRange);
我意识到这个内核的瓶颈是加权和的实现。如果有人能解释我如何改进它以使其更快,那将非常有帮助。
我可能没有正确使用不同的内存区域,我主要考虑的是我什至不使用的本地内存。
只是为了让您了解性能(即在 Nvidia GTX 660M 上),我将向您展示我取得的一些成绩。每个值是每层的神经元数量:
2500、10 000、2500:0.018s ~ 60FPS。它比我的处理器(运行在 2.40GHz 的 Intel Core i7)上快 4 到 5 倍
100 000, 100 000, 500: 140s -> 我想这并不奇怪,因为第二层中的每个神经元都必须执行 100 000 个元素的加权和。在我的处理器上运行它会产生大致相同的结果。
【问题讨论】:
-
您是在寻找针对 100k、100k、500 情况的优化,还是一般的性能提升?第一种情况(2500,10k,1500),第二种情况,还是其他一些输入大小范围,哪一种更常见?
-
这个问题比较笼统。我想第一种情况要常见得多。很少需要比这更多的神经元。这个想法更多是为了了解如何改进内核本身,也许可以更好地利用内存,优化循环......
标签: performance opencl