2012-05-24 88 views
19

Các hàm sigmoid được định nghĩa làNhanh thuật toán sigmoid

enter image description here

tôi thấy rằng bằng cách sử dụng C tích hợp chức năng exp() để tính giá trị của f(x) là chậm. Có bất kỳ thuật toán nhanh hơn để tính giá trị của f(x)?

Trả lời

15

bạn không phải sử dụng hàm sigmoid chính xác, thực tế trong thuật toán mạng nơron nhưng có thể thay thế bằng phiên bản xấp xỉ có thuộc tính tương tự nhưng nhanh hơn tính toán.

Ví dụ, bạn có thể sử dụng "sigmoid nhanh" chức năng

f(x) = x/(1 + abs(x)) 

Sử dụng thuật ngữ đầu tiên của việc mở rộng loạt cho exp (x) sẽ không giúp quá nhiều nếu các đối số f (x) là không gần bằng không, và bạn có cùng một vấn đề với việc mở rộng chuỗi hàm sigmoid nếu các đối số là "lớn".

Cách khác là sử dụng tra cứu bảng. Tức là, bạn tính toán trước các giá trị của hàm sigmoid cho một số điểm dữ liệu nhất định và sau đó thực hiện nội suy nhanh (tuyến tính) giữa chúng nếu bạn muốn.

-1

Tôi không nghĩ rằng bạn có thể làm tốt hơn điểm cuối được xây dựng() nhưng nếu bạn muốn một cách tiếp cận khác, bạn có thể sử dụng mở rộng chuỗi. WolframAlpha có thể tính toán nó cho bạn.

13

Tốt nhất nên đo lường phần cứng của bạn trước tiên. Chỉ cần một tiêu chuẩn nhanh chóng script cho thấy, rằng trên máy tính của tôi 1/(1+|x|) là nhanh nhất, và tanh(x) là đóng thứ hai. Chức năng lỗi erf cũng khá nhanh.

% gcc -Wall -O2 -lm -o sigmoid-bench{,.c} -std=c99 && ./sigmoid-bench 
atan(pi*x/2)*2/pi 24.1 ns 
atan(x)    23.0 ns 
1/(1+exp(-x))  20.4 ns 
1/sqrt(1+x^2)  13.4 ns 
erf(sqrt(pi)*x/2) 6.7 ns 
tanh(x)    5.5 ns 
x/(1+|x|)   5.5 ns 

Tôi hy vọng rằng kết quả có thể thay đổi tùy theo kiến ​​trúc và trình biên dịch sử dụng, nhưng erf(x) (từ C99), tanh(x)x/(1.0+fabs(x)) có thể sẽ là người biểu diễn nhanh.

+1

Cũng tin rằng bạn muốn nói 'x/sqrt (1 + x^2)' thay vì '1/sqrt (1 + x^2)'. – pqn

6

Để làm NN linh hoạt hơn thường được sử dụng một số tỷ lệ alpha để thay đổi góc của đồ thị xung quanh 0.

Các hàm sigmoid trông giống như:

f(x) = 1/(1+exp(-x*alpha)) 

Chức năng gần tương đương, (nhưng nhanh hơn) là:

f(x) = 0.5 * (x * alpha/(1 + abs(x*alpha))) + 0.5 

Bạn có thể kiểm tra các đồ thị here

Khi tôi sử dụng chức năng abs mạng trở nên nhanh hơn 100 lần.

+0

Khung tròn đầu tiên phải đóng ở phương trình thứ hai ở đâu? –

+0

Đã sửa lỗi, xem nội tuyến. – Nosyara

4

Câu trả lời này có thể không liên quan đến hầu hết các trường hợp, nhưng chỉ muốn ném ra khỏi đó để tính toán CUDA, tôi đã tìm thấy x/sqrt(1+x^2) là chức năng nhanh nhất cho đến nay.

Ví dụ, thực hiện với intrinsics phao chính xác duy nhất:

__device__ void fooCudaKernel(/* some arguments */) { 
    float foo, sigmoid; 
    // some code defining foo 
    sigmoid = __fmul_rz(rsqrtf(__fmaf_rz(foo,foo,1)),foo); 
} 
+0

Tốt. Mặc dù sẽ chỉ là nếu bạn tính toán các nơron như một Ma trận được kết nối hoàn toàn không phải là một Vector cho một hàng đơn/Ma trận thưa thớt. – user1496062

1

Sử dụng Eureqa để tìm kiếm xấp xỉ để sigmoid tôi thấy 1/(1 + 0.3678749025^x) xấp xỉ nó.Nó khá gần, chỉ cần loại bỏ một hoạt động với sự phủ định của x.

Một số chức năng khác được hiển thị ở đây là thú vị, nhưng hoạt động điện năng có thực sự chậm không? Tôi đã thử nghiệm nó và nó thực sự đã làm nhanh hơn so với bổ sung, nhưng đó chỉ có thể là một con sáo. Nếu như vậy, nó sẽ nhanh hơn hoặc nhanh hơn tất cả những người khác.

CHỈNH SỬA: 0.5 + 0.5*tanh(0.5*x) và ít chính xác hơn, 0.5 + 0.5*tanh(n) cũng hoạt động. Và bạn có thể loại bỏ các hằng số nếu bạn không quan tâm đến việc lấy nó giữa phạm vi [0,1] như sigmoid. Nhưng nó giả định rằng tanh nhanh hơn.

+0

Thuật ngữ nguồn thường chậm để thực thi có, do đó xấp xỉ này không tránh được khía cạnh của câu hỏi gốc, vì pow() thường được thực hiện trong mạch CPU như một điều chỉnh cho phép thực hiện/đánh giá exp(). – redcalx

8

Mọi người ở đây chủ yếu quan tâm đến chức năng của một chức năng tương đối nhanh như thế nào và tạo điểm chuẩn vi mô để xem liệu f1(x) có chạy 0,0001 ms nhanh hơn f2(x) hay không. Vấn đề lớn là điều này hầu như không liên quan, bởi vì điều quan trọng là mạng của bạn học được bao nhiêu với chức năng kích hoạt của bạn đang cố gắng giảm thiểu chức năng chi phí của bạn.

Tính đến lý thuyết hiện nay, rectifier function and softplusenter image description here

so với hàm sigmoid hoặc các chức năng kích hoạt tương tự, cho phép cho nhanh hơn và hiệu quả đào tạo của kiến ​​trúc thần kinh sâu trên bộ dữ liệu lớn và phức tạp.

Vì vậy, tôi khuyên bạn nên vứt bỏ tối ưu hóa vi mô và xem xét chức năng nào cho phép học nhanh hơn (cũng xem xét chức năng chi phí khác).

3

Ngoài ra bạn có thể sử dụng phiên bản thô của sigmoid (nó khác biệt không lớn hơn 0,2% so với ban đầu):

inline float RoughSigmoid(float value) 
    { 
     float x = ::abs(value); 
     float x2 = x*x; 
     float e = 1.0f + x + x2*0.555f + x2*x2*0.143f; 
     return 1.0f/(1.0f + (value > 0 ? 1.0f/e : e)); 
    } 

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst) 
    { 
     float s = slope[0]; 
     for (size_t i = 0; i < size; ++i) 
      dst[i] = RoughSigmoid(src[i] * s); 
    } 

Tối ưu hóa chức năng RoughSigmoid với việc sử dụng SSE:

#include <xmmintrin.h> 

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst) 
    { 
     size_t alignedSize = size/4*4; 
     __m128 _slope = _mm_set1_ps(*slope); 
     __m128 _0 = _mm_set1_ps(-0.0f); 
     __m128 _1 = _mm_set1_ps(1.0f); 
     __m128 _0555 = _mm_set1_ps(0.555f); 
     __m128 _0143 = _mm_set1_ps(0.143f); 
     size_t i = 0; 
     for (; i < alignedSize; i += 4) 
     { 
      __m128 _src = _mm_loadu_ps(src + i); 
      __m128 x = _mm_andnot_ps(_0, _mm_mul_ps(_src, _slope)); 
      __m128 x2 = _mm_mul_ps(x, x); 
      __m128 x4 = _mm_mul_ps(x2, x2); 
      __m128 series = _mm_add_ps(_mm_add_ps(_1, x), _mm_add_ps(_mm_mul_ps(x2, _0555), _mm_mul_ps(x4, _0143))); 
      __m128 mask = _mm_cmpgt_ps(_src, _0); 
      __m128 exp = _mm_or_ps(_mm_and_ps(_mm_rcp_ps(series), mask), _mm_andnot_ps(mask, series)); 
      __m128 sigmoid = _mm_rcp_ps(_mm_add_ps(_1, exp)); 
      _mm_storeu_ps(dst + i, sigmoid); 
     } 
     for (; i < size; ++i) 
      dst[i] = RoughSigmoid(src[i] * slope[0]); 
    } 

Tối ưu hóa chức năng RoughSigmoid với việc sử dụng AVX:

#include <immintrin.h> 

    void RoughSigmoid(const float * src, size_t size, const float * slope, float * dst) 
    { 
     size_t alignedSize = size/8*8; 
     __m256 _slope = _mm256_set1_ps(*slope); 
     __m256 _0 = _mm256_set1_ps(-0.0f); 
     __m256 _1 = _mm256_set1_ps(1.0f); 
     __m256 _0555 = _mm256_set1_ps(0.555f); 
     __m256 _0143 = _mm256_set1_ps(0.143f); 
     size_t i = 0; 
     for (; i < alignedSize; i += 8) 
     { 
      __m256 _src = _mm256_loadu_ps(src + i); 
      __m256 x = _mm256_andnot_ps(_0, _mm256_mul_ps(_src, _slope)); 
      __m256 x2 = _mm256_mul_ps(x, x); 
      __m256 x4 = _mm256_mul_ps(x2, x2); 
      __m256 series = _mm256_add_ps(_mm256_add_ps(_1, x), _mm256_add_ps(_mm256_mul_ps(x2, _0555), _mm256_mul_ps(x4, _0143))); 
      __m256 mask = _mm256_cmp_ps(_src, _0, _CMP_GT_OS); 
      __m256 exp = _mm256_or_ps(_mm256_and_ps(_mm256_rcp_ps(series), mask), _mm256_andnot_ps(mask, series)); 
      __m256 sigmoid = _mm256_rcp_ps(_mm256_add_ps(_1, exp)); 
      _mm256_storeu_ps(dst + i, sigmoid); 
     } 
     for (; i < size; ++i) 
      dst[i] = RoughSigmoid(src[i] * slope[0]); 
    } 
1

Chức năng tanh có thể được tối ưu hóa i n một số ngôn ngữ, làm cho nó nhanh hơn so với một x/(1 + abs (x) tùy chỉnh được xác định, như vậy là trường hợp trong Julia.

Các vấn đề liên quan