DarkNet 시리즈 - Blas
blas
blas 란?
벡터의 덧셈, 내적, 선형 조합, 행렬 곱셈과 같은 일반적인 선형 대수 연산을 수행하기 위한 역할을 합니다.
- Basic Linear Algebra Subprograms의 약자입니다.
- 크게 3개의 level(
vector-vector
,matrix-vector
,matrix-matrix
)로 구분되어 집니다.
copy_cpu
1
2
3
4
5
void copy_cpu(int N, float *X, int INCX, float *Y, int INCY)
{
int i;
for(i = 0; i < N; ++i) Y[i*INCY] = X[i*INCX];
}
함수 이름: copy_cpu
입력:
- N: 복사할 요소 수
- X: 복사할 데이터의 포인터
- INCX: X의 인덱스 간격
- Y: 복사 대상 데이터의 포인터
- INCY: Y의 인덱스 간격
동작:
- X의 요소를 Y에 복사합니다.
- 각각의 요소는 X의 인덱스 간격 INCX와 Y의 인덱스 간격 INCY를 사용하여 복사됩니다.
설명:
- 위 코드는 copy_cpu 함수입니다. 이 함수는 X 포인터가 가리키는 데이터를 Y 포인터가 가리키는 위치에 복사합니다. 이때 INCX와 INCY를 이용하여 X와 Y의 요소 간격을 조절할 수 있습니다.
- 이 함수는 주로 딥 러닝에서 사용되는 배열 연산에서 많이 활용됩니다.
mean_cpu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void mean_cpu(float *x, int batch, int filters, int spatial, float *mean)
{
float scale = 1./(batch * spatial);
int i,j,k;
for(i = 0; i < filters; ++i){
mean[i] = 0;
for(j = 0; j < batch; ++j){
for(k = 0; k < spatial; ++k){
int index = j*filters*spatial + i*spatial + k;
mean[i] += x[index];
}
}
mean[i] *= scale;
}
}
함수 이름: mean_cpu
입력:
- x: 입력 데이터의 포인터
- batch: 배치 크기
- filters: 필터 수
- spatial: 공간 크기
- mean: 평균값을 저장할 포인터
동작:
- x의 요소들을 이용하여 각 필터의 평균값을 계산합니다.
- mean 포인터가 가리키는 위치에 각 필터의 평균값을 저장합니다.
설명:
- 위 코드는 mean_cpu 함수입니다. 이 함수는 입력 데이터 x의 요소들을 이용하여 각 필터의 평균값을 계산하고, 그 결과를 mean 포인터가 가리키는 위치에 저장합니다.
- 평균값은 배치 크기, 필터 수, 공간 크기를 고려하여 계산되며, 배치 크기와 공간 크기를 곱한 값의 역수를 scale 변수에 저장하여 평균값 계산에 사용합니다.
- 이 함수는 주로 딥 러닝에서 사용되는 배열 연산에서 많이 활용됩니다.
variance_cpu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void variance_cpu(float *x, float *mean, int batch, int filters, int spatial, float *variance)
{
float scale = 1./(batch * spatial - 1);
int i,j,k;
for(i = 0; i < filters; ++i){
variance[i] = 0;
for(j = 0; j < batch; ++j){
for(k = 0; k < spatial; ++k){
int index = j*filters*spatial + i*spatial + k;
variance[i] += pow((x[index] - mean[i]), 2);
}
}
variance[i] *= scale;
}
}
함수 이름: variance_cpu
입력:
- x: 입력 데이터
- mean: 입력 데이터의 평균 값
- batch: 배치 크기
- filters: 필터의 개수
- spatial: 공간 차원(너비와 높이)
동작:
- 모든 배치, 필터 및 공간 요소에 대해, 입력 데이터 x의 해당 필터 요소와 평균 값 간의 차이를 계산하고, 그 차이의 제곱을 누적하여 분산 값을 계산합니다.
- 계산된 분산 값을 배치 크기 및 공간 차원에서 감소된 자유도에 따라 스케일링합니다.
설명:
- 위 코드는 입력 데이터의 분산 값을 계산하는 함수인 variance_cpu입니다. 분산은 데이터가 얼마나 분산되어 있는지를 나타내는 지표이며, 입력 데이터의 분산 값은 딥 러닝에서 Batch Normalization 등과 같은 기술에서 자주 사용됩니다.
- 이 함수는 입력 데이터 x와 해당 필터의 평균 값을 이용하여 분산 값을 계산하고, 스케일링을 통해 반환합니다.
normalize_cpu
1
2
3
4
5
6
7
8
9
10
11
12
void normalize_cpu(float *x, float *mean, float *variance, int batch, int filters, int spatial)
{
int b, f, i;
for(b = 0; b < batch; ++b){
for(f = 0; f < filters; ++f){
for(i = 0; i < spatial; ++i){
int index = b*filters*spatial + f*spatial + i;
x[index] = (x[index] - mean[f])/(sqrt(variance[f]) + .000001f);
}
}
}
}
함수 이름: normalize_cpu
입력:
- x: 정규화할 데이터의 포인터
- mean: 평균 값의 포인터
- variance: 분산 값의 포인터
- batch: 배치 크기
- filters: 필터 개수
- spatial: 공간 크기
동작:
- 입력으로 받은 데이터 x를 정규화하여 각 요소에 대해 평균 값을 빼고, 표준 편차 값으로 나누어 저장합니다.
- 데이터 x는 입력 인덱스(b, f, i)에 따라서 정규화되어 저장됩니다.
설명:
- 위 코드는 딥 러닝에서 많이 사용되는 정규화 함수인 normalize_cpu입니다. 이 함수는 입력된 데이터의 평균과 분산을 이용하여 데이터를 정규화하는데 사용됩니다.
- 이 함수는 각 요소를 해당 필터의 평균 값으로 뺀 후, 해당 필터의 분산 값으로 나누어 정규화합니다.
- 이 함수를 사용하면 모델이 더 빠르고 안정적으로 수렴하게 되므로, 딥 러닝 모델에서 많이 활용됩니다.
axpy_cpu
1
2
3
4
5
void axpy_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY)
{
int i;
for(i = 0; i < N; ++i) Y[i*INCY] += ALPHA*X[i*INCX];
}
함수 이름: axpy_cpu
입력:
- N: X 및 Y에서 복사해야 할 요소 수
- ALPHA: X에서 Y로 복사될 때 곱해지는 스칼라 값
- X: 복사할 데이터가 저장된 원본 배열
- INCX: X의 인덱스 간격
- Y: 데이터가 복사될 대상 배열
- INCY: Y의 인덱스 간격
동작:
- X의 요소를 Y에 복사하고 ALPHA 값으로 곱한 다음 Y에 더합니다.
- 각각의 요소는 X의 인덱스 간격 INCX와 Y의 인덱스 간격 INCY를 사용하여 복사됩니다.
설명:
- 위 코드는 axpy_cpu 함수입니다. 이 함수는 X 포인터가 가리키는 데이터를 ALPHA 값을 곱한 후 Y 포인터가 가리키는 위치에 더합니다. 이때 INCX와 INCY를 이용하여 X와 Y의 요소 간격을 조절할 수 있습니다.
- 이 함수는 주로 딥 러닝에서 사용되는 배열 연산에서 많이 활용됩니다.
scal_cpu
1
2
3
4
5
void scal_cpu(int N, float ALPHA, float *X, int INCX)
{
int i;
for(i = 0; i < N; ++i) X[i*INCX] *= ALPHA;
}
함수 이름: scal_cpu
입력:
- N: 스칼라를 적용할 벡터 X의 크기
- ALPHA: 스칼라 값
- X: 스칼라 값을 적용할 벡터의 포인터
- INCX: X의 인덱스 간격
동작:
- 벡터 X의 각 요소에 스칼라 값 ALPHA를 곱해 갱신합니다. 이때 INCX를 이용하여 X의 인덱스 간격을 조절할 수 있습니다.
설명:
- 위 코드는 scal_cpu 함수입니다. 이 함수는 주로 딥 러닝에서 사용되는 배열 연산에서 많이 활용됩니다.
- 벡터 X의 각 요소에 스칼라 값을 곱해 갱신하는 것은 배열의 크기를 변경하는 것이 아니라, 벡터를 스칼라 값으로 스케일링하는 것을 의미합니다. 따라서 이 함수는 주로 데이터 전처리나 최적화 알고리즘에서 사용됩니다.
fill_cpu
1
2
3
4
5
void fill_cpu(int N, float ALPHA, float *X, int INCX)
{
int i;
for(i = 0; i < N; ++i) X[i*INCX] = ALPHA;
}
함수 이름: fill_cpu
입력:
- N: 채워질 요소 수
- ALPHA: 배열에 할당할 값
- X: 채워질 데이터의 포인터
- INCX: X의 인덱스 간격
동작:
- X 배열에 ALPHA 값을 채웁니다.
- 각 요소는 X의 인덱스 간격 INCX를 사용하여 채워집니다.
설명:
- 위 코드는 fill_cpu 함수입니다. 이 함수는 X 포인터가 가리키는 배열에 ALPHA 값으로 모든 요소를 채웁니다. 이때 INCX를 이용하여 X 배열의 요소 간격을 조절할 수 있습니다.
- 이 함수는 주로 딥 러닝에서 초기화나 전처리 과정에서 사용됩니다.
mul_cpu
1
2
3
4
5
void mul_cpu(int N, float *X, int INCX, float *Y, int INCY)
{
int i;
for(i = 0; i < N; ++i) Y[i*INCY] *= X[i*INCX];
}
함수 이름: mul_cpu
입력:
- N: 요소 수
- X: 곱해질 데이터의 포인터
- INCX: X의 인덱스 간격
- Y: 곱하는 대상 데이터의 포인터
- INCY: Y의 인덱스 간격
동작:
- Y의 각 요소에 X의 해당 요소를 곱합니다.
- 각 요소는 X와 Y의 인덱스 간격 INCX와 INCY를 사용하여 계산됩니다.
설명:
- 위 코드는 mul_cpu 함수입니다. 이 함수는 X 포인터가 가리키는 데이터와 Y 포인터가 가리키는 대상 데이터를 요소별로 곱합니다. 이때 INCX와 INCY를 이용하여 X와 Y의 요소 간격을 조절할 수 있습니다.
- 이 함수는 주로 딥 러닝에서 사용되는 배열 연산에서 많이 활용됩니다.
pow_cpu
1
2
3
4
5
void pow_cpu(int N, float ALPHA, float *X, int INCX, float *Y, int INCY)
{
int i;
for(i = 0; i < N; ++i) Y[i*INCY] = pow(X[i*INCX], ALPHA);
}
함수 이름: pow_cpu
입력:
- N: 처리할 데이터 개수
- ALPHA: 거듭제곱 계산에 사용할 지수
- X: 입력 데이터 배열 포인터
- INCX: 입력 데이터 배열에서 원소 사이의 간격
- Y: 출력 데이터 배열 포인터
- INCY: 출력 데이터 배열에서 원소 사이의 간격
동작:
- 입력 데이터 배열 X에서 원소를 ALPHA 지수만큼 거듭제곱하여 출력 데이터 배열 Y에 저장함
설명:
- pow_cpu 함수는 입력 데이터 배열 X에서 원소를 ALPHA 지수만큼 거듭제곱하여 출력 데이터 배열 Y에 저장하는 함수입니다.
- 이 함수는 N 개의 원소를 처리하며, INCX와 INCY를 이용해 X와 Y 배열의 포인터를 이동하면서 원소에 접근합니다.
deinter_cpu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void deinter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT)
{
int i, j;
int index = 0;
for(j = 0; j < B; ++j) {
for(i = 0; i < NX; ++i){
if(X) X[j*NX + i] += OUT[index];
++index;
}
for(i = 0; i < NY; ++i){
if(Y) Y[j*NY + i] += OUT[index];
++index;
}
}
}
함수 이름: deinter_cpu
입력:
- NX: 인터리브된 X 데이터의 수
- X: 인터리브된 X 데이터 (NX*B 길이)
- NY: 인터리브된 Y 데이터의 수
- Y: 인터리브된 Y 데이터 (NY*B 길이)
- B: 배치 크기
- OUT: 인터리브된 출력 데이터 (NX_NY_B 길이)
동작:
- 인터리브된 출력 데이터에서 X와 Y 데이터를 추출하여 X와 Y에 더해줌
- OUT은 인터리브된 형태로 저장된 출력 데이터이며, B개의 배치에 대한 X와 Y 데이터가 번갈아가며 저장되어 있다.
설명:
- 이 함수는 인터리브(interleave)된 데이터를 디인터리브(deinterleave)하여 X와 Y 데이터로 다시 나누어 주는 함수이다.
- 예를 들어, 인터리브된 데이터는 [x1, y1, x2, y2, x3, y3, …]와 같이 X와 Y 데이터가 번갈아가며 저장되어 있으며, 이를 X=[x1, x2, x3, …], Y=[y1, y2, y3, …]로 나누어 주는 것이다.
- 이 함수는 X와 Y를 입력으로 받아서 각각 NX와 NY의 길이를 가지는 데이터를 B개 만큼 받아온다. 이때, OUT은 인터리브된 형태로 저장된 출력 데이터이다.
- 함수는 B개의 배치에 대해 for 루프를 수행하며, 각 배치에서 NX개의 X 데이터와 NY개의 Y 데이터를 추출하여, 인터리브된 형태로 저장된 OUT에서 순서대로 읽어와서 X와 Y에 더해준다.
inter_cpu
1
2
3
4
5
6
7
8
9
10
11
12
13
void inter_cpu(int NX, float *X, int NY, float *Y, int B, float *OUT)
{
int i, j;
int index = 0;
for(j = 0; j < B; ++j) {
for(i = 0; i < NX; ++i){
OUT[index++] = X[j*NX + i];
}
for(i = 0; i < NY; ++i){
OUT[index++] = Y[j*NY + i];
}
}
}
함수 이름: inter_cpu
입력:
- NX: int 타입 변수. 배열 X의 요소 개수.
- X: float 타입 배열. NX개의 요소를 가진 배열.
- NY: int 타입 변수. 배열 Y의 요소 개수.
- Y: float 타입 배열. NY개의 요소를 가진 배열.
- B: int 타입 변수. 배치 크기.
- OUT: float 타입 배열. NX+NY개의 요소를 가진 배열.
동작:
- 입력으로 주어진 X와 Y 배열을 섞어서 OUT 배열에 저장한다.
- 배치 사이즈 B만큼 X, Y 배열을 섞어서 OUT 배열에 저장한다.
- OUT 배열의 크기는 (NX+NY)*B이다.
설명:
- 이 함수는 YOLOv3 딥러닝 네트워크에서 사용되는 함수 중 하나이다.
- 이 함수는 배열 X와 배열 Y의 각 요소를 번갈아가며 OUT 배열에 저장하는 역할을 한다.
- 배열 X와 배열 Y는 각각 다른 데이터를 가지고 있으며, 이 함수는 이들을 하나의 배열로 섞어서 사용한다.
- 배열 X와 Y는 입력 이미지를 처리하기 위한 레이어에서 사용되며, inter_cpu 함수는 이들을 결합해주는 역할을 한다.
- 이 함수는 입력 데이터를 처리하는 데 있어서 필수적인 역할을 수행하며, 딥러닝 네트워크의 정확도와 속도에 직접적인 영향을 미친다.
mult_add_into_cpu
1
2
3
4
5
void mult_add_into_cpu(int N, float *X, float *Y, float *Z)
{
int i;
for(i = 0; i < N; ++i) Z[i] += X[i]*Y[i];
}
함수 이름: mult_add_into_cpu
입력:
- N: 정수값으로, X, Y, Z 배열의 길이
- X: float 형태의 배열 포인터
- Y: float 형태의 배열 포인터
- Z: float 형태의 배열 포인터
동작:
- Z 배열에 X[i]*Y[i] 값을 더한다. i는 0부터 N-1까지 반복한다.
설명:
- mult_add_into_cpu 함수는 X, Y, Z 배열에서 같은 인덱스 값을 곱한 다음, 그 결과를 Z 배열의 같은 인덱스에 더하는 함수이다.
- 이 함수는 벡터 내적과 비슷한 역할을 하며, 두 배열의 요소 곱의 합을 계산하는 것이다.
- 이 함수는 다양한 수학 연산에서 사용될 수 있으며, 행렬 연산에서 행렬의 각 행과 열을 곱한 뒤 합한 값을 계산하는 데에도 사용될 수 있다.
smooth_l1_cpu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void smooth_l1_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float diff = truth[i] - pred[i];
float abs_val = fabs(diff);
if(abs_val < 1) {
error[i] = diff * diff;
delta[i] = diff;
}
else {
error[i] = 2*abs_val - 1;
delta[i] = (diff < 0) ? 1 : -1;
}
}
}
함수 이름: smooth_l1_cpu
입력:
- n: 배열의 길이
- pred: 예측값
- truth: 실제값
- delta: gradient
- error: 에러
동작:
- 예측값(pred)과 실제값(truth) 사이의 차이(diff)를 구하고, 차이의 절댓값(abs_val)이 1보다 작으면 error와 delta를 계산하여 반환하고, 그렇지 않으면 error와 delta를 다시 계산하여 반환한다.
설명:
- 이 함수는 smooth L1 손실 함수를 계산하는 데 사용된다.
- 이 손실 함수는 회귀(regression) 문제에서 많이 사용되며, 주어진 입력 값에 대해 실제 값과 예측 값을 비교하여 오차를 계산한다.
- smooth L1 손실 함수는 L2 손실 함수(MSE)와 L1 손실 함수(MAE)를 결합한 것으로, 오차가 크면 L1 손실 함수처럼 절댓값을 사용하고, 작으면 L2 손실 함수처럼 제곱을 사용한다.
- 이 함수는 CPU에서 동작하며, 각각의 입력값에 대해 error와 delta를 계산하여 반환한다.
l1_cpu
1
2
3
4
5
6
7
8
9
void l1_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float diff = truth[i] - pred[i];
error[i] = fabs(diff);
delta[i] = diff > 0 ? 1 : -1;
}
}
함수 이름: l1_cpu
입력:
- n: 데이터 샘플 수
- pred: 예측값 배열
- truth: 실제값 배열
- delta: 역전파 시 계산되는 델타값이 저장될 배열
- error: 손실값이 저장될 배열
동작:
- 예측값 배열 pred와 실제값 배열 truth 간의 L1 손실을 계산하여 error 배열에 저장하고,
- 예측값과 실제값의 차이가 양수인 경우 delta 배열에 1, 음수인 경우 -1을 저장함으로써 역전파 시 계산되는 델타값을 구한다.
설명:
- L1 손실 함수는 예측값과 실제값 간의 차이의 절대값을 손실값으로 사용한다.
- L1 손실은 이상치(Outlier)에 대한 민감도가 높아 이상치가 적은 데이터에서는 MSE 손실보다 더 좋은 성능을 보인다.
- 이 함수는 CPU에서 동작하며, 입력으로 주어진 예측값과 실제값의 차이를 계산하여 손실값과 델타값을 구한다.
softmax_x_ent_cpu
1
2
3
4
5
6
7
8
9
10
void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float t = truth[i];
float p = pred[i];
error[i] = (t) ? -log(p) : 0;
delta[i] = t-p;
}
}
함수 이름: softmax_x_ent_cpu
입력:
- n: 예측값(pred)과 실제값(truth)의 개수
- pred: 예측값 포인터
- truth: 실제값 포인터
- delta: 역전파 시 오차값을 저장할 포인터
- error: 손실값을 저장할 포인터
동작:
- 소프트맥스(softmax) 함수와 크로스 엔트로피(cross-entropy) 손실 함수를 계산하여 오차값과 손실값을 계산합니다.
설명:
- 이 함수는 딥러닝에서 분류(classification) 문제에서 사용하는 손실 함수 중 하나인 크로스 엔트로피(cross-entropy) 손실 함수를 계산합니다. 이 손실 함수는 소프트맥스 함수와 함께 분류 문제에서 널리 사용됩니다.
- 함수의 동작은 다음과 같습니다. 예측값(pred)과 실제값(truth)이 주어졌을 때, 먼저 소프트맥스 함수를 이용해 예측값을 확률값으로 변환합니다. 그 다음, 확률값과 실제값을 이용해 크로스 엔트로피 손실 함수를 계산합니다. 손실 함수의 값이 작을수록 모델의 예측값이 실제값에 가깝다는 것을 의미합니다.
- 이 함수는 역전파(backpropagation)를 위해 오차값(delta)도 함께 계산합니다. 역전파를 통해 이 오차값이 모델의 파라미터를 업데이트할 때 사용됩니다.
logistic_x_ent_cpu
1
2
3
4
5
6
7
8
9
10
void logistic_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float t = truth[i];
float p = pred[i];
error[i] = -t*log(p) - (1-t)*log(1-p);
delta[i] = t-p;
}
}
함수 이름: logistic_x_ent_cpu
입력:
- n: 예측 값(pred)과 실제 값(truth)의 길이
- pred: 예측 값 배열 포인터
- truth: 실제 값 배열 포인터
- delta: 델타 값 배열 포인터
- error: 오차 값 배열 포인터
동작:
- 로지스틱 회귀에서 크로스 엔트로피 손실 함수의 값을 계산하고, 그 값을 이용해 델타 값을 계산하는 함수이다.
설명:
이 함수는 n개의 원소를 갖는 pred, truth 배열의 원소 값들을 이용하여 로지스틱 회귀에서 크로스 엔트로피 손실 함수의 값을 계산하고, 그 값을 이용해 델타 값을 계산한다. 이 함수는 다음과 같은 작업을 수행한다.
- i번째 원소에 대해서, t는 실제 값(truth[i]), p는 예측 값(pred[i])이다.
- error[i]에는 크로스 엔트로피 손실 함수의 i번째 원소 값이 저장된다.
- delta[i]에는 델타 값(i번째 원소에 대한 예측 값과 실제 값의 차이)이 저장된다.
주의할 점은 pred와 truth는 확률값으로 간주되어야 한다는 것이다. 즉, 0과 1 사이의 값이어야 한다.
l2_cpu
1
2
3
4
5
6
7
8
9
void l2_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float diff = truth[i] - pred[i];
error[i] = diff * diff;
delta[i] = diff;
}
}
함수 이름: l2_cpu
입력:
- n: 예측 값(pred)과 실제 값(truth)의 원소 개수
- *pred: 예측 값 배열
- *truth: 실제 값 배열
- *delta: 예측 값과 실제 값의 차이
- *error: 예측 값과 실제 값의 차이의 제곱
동작:
- 예측 값(pred)과 실제 값(truth)을 이용하여 예측 값과 실제 값의 차이(diff)를 계산하고, 해당 차이(diff)를 이용하여 예측 값과 실제 값의 차이의 제곱(error)과 차이(delta)를 계산한다.
설명:
- l2(제곱근 오차) 손실 함수를 계산하는 함수로, 딥러닝에서 사용된다.
- l2 손실 함수는 예측 값과 실제 값의 차이를 제곱한 값들의 합을 구하는 방식으로 손실을 계산한다.
- 이를 이용하여 예측 값과 실제 값 사이의 오차를 계산하고, 이를 역전파(backpropagation)를 통해 학습을 진행한다.
dot_cpu
1
2
3
4
5
6
7
float dot_cpu(int N, float *X, int INCX, float *Y, int INCY)
{
int i;
float dot = 0;
for(i = 0; i < N; ++i) dot += X[i*INCX] * Y[i*INCY];
return dot;
}
함수 이름: dot_cpu
입력:
- N(벡터의 길이)
- X(첫번째 벡터)
- INCX(첫번째 벡터의 증분)
- Y(두번째 벡터)
- INCY(두번째 벡터의 증분)
동작:
- 두 벡터 X와 Y의 내적(dot product)을 계산한다. 즉, X[0]*Y[0] + X[INCX]*Y[INCY] + … + X[(N-1)*INCX]*Y[(N-1)*INCY]를 계산한다.
설명:
- 두 벡터의 내적은 선형 대수학에서 자주 사용되는 연산 중 하나이다.
- 이 함수는 N개의 원소를 가지는 두 벡터 X와 Y의 내적을 계산한다. X와 Y는 각각 INCX, INCY만큼 증분된다. 이 함수는 내적 결과를 반환한다.
softmax
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void softmax(float *input, int n, float temp, int stride, float *output)
{
int i;
float sum = 0;
float largest = -FLT_MAX;
for(i = 0; i < n; ++i){
if(input[i*stride] > largest) largest = input[i*stride];
}
for(i = 0; i < n; ++i){
float e = exp(input[i*stride]/temp - largest/temp);
sum += e;
output[i*stride] = e;
}
for(i = 0; i < n; ++i){
output[i*stride] /= sum;
}
}
함수 이름: softmax
입력:
- input: 소프트맥스 함수를 적용할 입력 배열 포인터
- n: 입력 배열의 크기
- temp: 소프트맥스 함수의 온도 매개변수
- stride: 입력 배열에서 요소 간의 간격
- output: 소프트맥스 함수의 출력 배열 포인터
동작:
- 입력 배열의 각 요소에 대해 소프트맥스 함수를 적용하여 출력 배열을 계산
- 입력 배열에서 가장 큰 값(largest)을 찾음
- 출력 배열의 각 요소를 계산할 때, largest로부터 temp로 나눈 값을 지수 함수의 입력으로 사용하여 지수 함수 계산
- 모든 요소의 지수 함수 값을 더하고, 각 요소의 지수 함수 값을 총합으로 나눠 정규화
- 최종적으로 정규화된 값을 출력 배열에 저장
설명:
- 소프트맥스 함수는 입력 배열을 확률 분포로 변환하는 함수이며, 각 입력 요소를 정규화된 확률 값으로 변환한다. softmax 함수는 크게 세 부분으로 구성된다.
- 먼저 입력 배열에서 가장 큰 값을 찾아 largest에 저장한다.
- 그 다음, 입력 배열의 각 요소에 대해 지수 함수를 계산하여 출력 배열을 구한다. 마지막으로 출력 배열의 모든 값을 더하고 총합으로 나눠 정규화한다.
- 이를 통해 출력 배열의 값은 모두 0과 1 사이에 있으며, 총합은 1이 된다.
- 이렇게 구해진 출력 배열은 입력 배열의 확률 분포를 나타낸다. 소프트맥스 함수는 딥러닝에서 주로 출력층에서 사용되며, 다중 클래스 분류 문제에서 사용된다.
softmax_cpu
1
2
3
4
5
6
7
8
9
void softmax_cpu(float *input, int n, int batch, int batch_offset, int groups, int group_offset, int stride, float temp, float *output)
{
int g, b;
for(b = 0; b < batch; ++b){
for(g = 0; g < groups; ++g){
softmax(input + b*batch_offset + g*group_offset, n, temp, stride, output + b*batch_offset + g*group_offset);
}
}
}
함수 이름: softmax_cpu
입력:
- input: 입력 배열의 포인터
- n: softmax를 수행할 원소 수
- batch: 배치 크기
- batch_offset: 배치 간격
- groups: 그룹 수
- group_offset: 그룹 간격
- stride: 스트라이드 크기
- temp: softmax scaling 매개변수
- output: 출력 배열의 포인터
동작:
- 배치와 그룹에 대해 softmax 함수를 적용합니다.
- 각각의 배치와 그룹에서, 입력 배열에서 해당 배치와 그룹을 선택하고, softmax 함수를 수행하여 출력 배열에 결과를 저장합니다.
설명:
- softmax 함수는 주어진 입력 배열의 값들을 정규화하여 출력 배열의 합이 1이 되도록 합니다. 이 함수는 주로 분류 문제에서 출력층에서 사용됩니다. softmax 함수는 각 원소의 지수값을 취한 후 전체 원소의 합으로 나누어 값을 정규화합니다. softmax_cpu 함수는 이러한 softmax 함수를 배치와 그룹 단위로 수행합니다.
- batch와 groups는 배열을 분할하는 데 사용됩니다. batch_offset은 한 배치의 원소 수와 stride를 곱한 값입니다. group_offset은 한 그룹의 원소 수와 stride를 곱한 값입니다. 이러한 배치 및 그룹의 설정은 입력 배열을 다룰 때 유용합니다.
- temp 매개변수는 softmax 함수의 스케일을 조절하는 데 사용됩니다. 출력 배열에서의 값을 모두 같은 범위 내에 유지하기 위해 사용됩니다. 이 값이 작으면 출력 배열의 차이가 커집니다.
- softmax_cpu 함수는 입력 배열의 각 배치와 그룹에 대해 softmax 함수를 수행하고 결과를 출력 배열에 저장합니다. 출력 배열의 크기는 입력 배열과 동일합니다.
upsample_cpu
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
void upsample_cpu(float *in, int w, int h, int c, int batch, int stride, int forward, float scale, float *out)
{
int i, j, k, b;
for(b = 0; b < batch; ++b){
for(k = 0; k < c; ++k){
for(j = 0; j < h*stride; ++j){
for(i = 0; i < w*stride; ++i){
int in_index = b*w*h*c + k*w*h + (j/stride)*w + i/stride;
int out_index = b*w*h*c*stride*stride + k*w*h*stride*stride + j*w*stride + i;
if(forward) out[out_index] = scale*in[in_index];
else in[in_index] += scale*out[out_index];
}
}
}
}
}
// if stride : 2
// [1, 2, 3, 4] --> [1, 1, 2, 2, 3, 3, 4, 4]
함수 이름: upsample_cpu
입력:
- float *in: 입력 데이터 포인터
- int w: 입력 데이터의 너비
- int h: 입력 데이터의 높이
- int c: 입력 데이터의 채널 수
- int batch: 배치 크기
- int stride: 업샘플링 스트라이드
- int forward: 순방향 여부(1이면 순방향, 0이면 역방향)
- float scale: 스케일링 계수
- float *out: 출력 데이터 포인터
동작:
- 입력 데이터를 업샘플링하여 출력 데이터를 생성합니다.
- 입력 데이터의 각 픽셀은 스트라이드 크기만큼 연속된 출력 데이터의 픽셀들에 복사됩니다.
- 업샘플링 스트라이드가 2인 경우, 입력 데이터의 (0,0) 위치는 출력 데이터의 (0,0), (0,1), (1,0), (1,1) 위치에 복사됩니다.
- 역방향 계산을 수행하는 경우, 출력 데이터의 변화를 입력 데이터에 역으로 전파합니다.
설명:
- upsample_cpu 함수는 입력 데이터를 업샘플링하여 출력 데이터를 생성하는 함수입니다.
- 이미지 분석 분야에서 영상 크기를 확대하거나 해상도를 높이는 등의 용도로 자주 사용됩니다.
- 입력 데이터는 4차원 텐서(batch, 채널, 높이, 너비)로 표현되며, 출력 데이터 역시 같은 크기의 4차원 텐서로 생성됩니다.
- 스트라이드 크기는 업샘플링 스트라이드 매개변수를 통해 지정할 수 있으며, 스케일링 계수(scale)를 통해 출력 데이터의 값 범위를 조정할 수 있습니다.
reorg_cpu
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
void reorg_cpu(float *x, int w, int h, int c, int batch, int stride, int forward, float *out)
{
int b,i,j,k;
int out_c = c/(stride*stride);
for(b = 0; b < batch; ++b){
for(k = 0; k < c; ++k){
for(j = 0; j < h; ++j){
for(i = 0; i < w; ++i){
int in_index = i + w*(j + h*(k + c*b));
int c2 = k % out_c;
int offset = k / out_c;
int w2 = i*stride + offset % stride;
int h2 = j*stride + offset / stride;
int out_index = w2 + w*stride*(h2 + h*stride*(c2 + out_c*b));
if(forward) out[out_index] = x[in_index];
else out[in_index] = x[out_index];
}
}
}
}
}
// if | w : 2 | h : 2 | c : 4 | stride : 2 |
// [ 1, 2, 3, 4] [ 1, 5, 2, 6]
// [ 5, 6, 7, 8] -- [ 9, 13, 10, 14]
// [ 9, 10, 11, 12] -- [ 3, 7, 4, 8]
// [13, 14, 15, 16] [11, 15, 12, 16]
함수 이름: reorg_cpu
입력:
- float *x: 입력 배열 포인터
- int w: 입력 배열의 너비
- int h: 입력 배열의 높이
- int c: 입력 배열의 채널 수
- int batch: 입력 배열의 배치 크기
- int stride: reorg 작업의 stride 값
- int forward: reorg 작업의 방향. forward는 1, backward는 0
- float *out: 출력 배열 포인터
동작:
- 입력 배열 x를 reorg 작업으로 출력 배열 out으로 변환하는 함수.
- reorg 작업은 입력 배열의 크기를 줄이거나 늘리는 작업을 수행하며, stride 값에 따라 크기가 달라진다.
- forward 값이 1일 경우, 입력 배열 x를 출력 배열 out으로 변환한다.
- forward 값이 0일 경우, 출력 배열 out을 입력 배열 x로 변환한다.
- 입력 배열 x의 각 요소는 출력 배열의 새로운 위치로 이동한다.
- reorg 작업은 다음과 같은 공식으로 수행된다:
- out_index = w2 + w * stride * (h2 + h * stride * (c2 + out_c * b))
- in_index = i + w * (j + h * (k + c * b))
- c2 = k % out_c
- offset = k / out_c
- w2 = i * stride + offset % stride
- h2 = j * stride + offset / stride
설명:
- 입력 배열 x는 4차원 배열로(batch, channel, height, width) 주로 이미지 데이터를 다룰 때 사용된다.
- reorg 작업은 YOLO(Object Detection 알고리즘)에서 사용되는 작업 중 하나로, feature map을 다른 크기로 변환하여 다른 크기의 feature map과 결합하는 작업을 수행한다.
- 이 함수는 CPU 상에서 실행된다.
flatten
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
void flatten(float *x, int size, int layers, int batch, int forward)
{
float *swap = calloc(size*layers*batch, sizeof(float));
int i,c,b;
for(b = 0; b < batch; ++b){
for(c = 0; c < layers; ++c){
for(i = 0; i < size; ++i){
int i1 = b*layers*size + c*size + i;
int i2 = b*layers*size + i*layers + c;
if (forward) swap[i2] = x[i1];
else swap[i1] = x[i2];
}
}
}
memcpy(x, swap, size*layers*batch*sizeof(float));
free(swap);
}
함수 이름: flatten
입력:
- float *x: 1차원 배열의 포인터
- int size: 1차원 배열의 크기
- int layers: 2차원 배열의 열의 수
- int batch: 3차원 배열의 배치 크기
- int forward: 1 또는 0 값. 1이면 3차원 배열을 2차원 배열로 펼침. 0이면 2차원 배열을 3차원 배열로 펼침.
동작:
- 3차원 배열을 2차원 배열로 또는 2차원 배열을 3차원 배열로 변환하는 함수
- 3차원 배열의 데이터를 2차원 배열의 형태로 재배치하여, 포인터 swap에 저장
- forward가 1이면 3차원 배열을 2차원 배열로 펼치는 경우이므로, swap의 값을 x로 복사.
- forward가 0이면 2차원 배열을 3차원 배열로 펼치는 경우이므로, swap의 값을 x로 역으로 복사.
- swap 배열을 동적으로 할당하여 사용하므로, 마지막에는 free 함수로 메모리를 해제.
설명:
- 다차원 배열에서 데이터의 순서를 변경하는 함수
- 특히, 딥 러닝에서 입력 데이터를 2차원 배열의 형태로 펼치는 flatten 레이어에서 사용됨.
- 3차원 배열을 2차원 배열로 펼칠 때, 데이터가 가로 방향으로 2차원 배열의 열의 수만큼 연속하게 배치됨.
- 2차원 배열을 3차원 배열로 변환할 때, 데이터가 세로 방향으로 2차원 배열의 열의 수만큼 연속하게 배치됨.
weighted_sum_cpu
1
2
3
4
5
6
7
void weighted_sum_cpu(float *a, float *b, float *s, int n, float *c)
{
int i;
for(i = 0; i < n; ++i){
c[i] = s[i]*a[i] + (1-s[i])*(b ? b[i] : 0);
}
}
함수 이름: weighted_sum_cpu
입력:
- float *a: 첫 번째 입력 배열
- float *b: 두 번째 입력 배열
- float *s: 가중치 배열
- int n: 배열의 크기
- float *c: 결과를 저장할 출력 배열
동작:
- 배열 a, b, s를 입력으로 받아 가중 평균 계산을 통해 c 배열을 계산한다.
- 배열 b가 NULL이 아닌 경우, (1-s[i])*b[i]를 더해준다.
설명:
- 두 개의 배열 a, b와 가중치 배열 s를 이용해 가중 평균 계산을 수행하는 함수이다.
- 결과값은 출력 배열 c에 저장된다.
- 만약 b 배열이 NULL이라면, 두 번째 항은 계산되지 않는다.
weighted_delta_cpu
1
2
3
4
5
6
7
8
9
void weighted_delta_cpu(float *a, float *b, float *s, float *da, float *db, float *ds, int n, float *dc)
{
int i;
for(i = 0; i < n; ++i){
if(da) da[i] += dc[i] * s[i];
if(db) db[i] += dc[i] * (1-s[i]);
ds[i] += dc[i] * (a[i] - b[i]);
}
}
함수 이름: weighted_delta_cpu
입력:
- a: float형 배열 포인터
- b: float형 배열 포인터
- s: float형 배열 포인터
- da: float형 배열 포인터
- db: float형 배열 포인터
- ds: float형 배열 포인터
- n: int형 변수
- dc: float형 배열 포인터
동작: 입력으로 받은 배열과 변수들을 사용하여 가중치 계산을 수행합니다.
설명: 이 함수는 뉴럴 네트워크에서 가중치 계산을 수행하는 함수입니다. 입력으로는 두 개의 float형 배열 포인터(a, b), 한 개의 float형 배열 포인터(s), 세 개의 float형 배열 포인터(da, db, ds), int형 변수 n, 그리고 한 개의 float형 배열 포인터 dc가 필요합니다.
이 함수는 입력으로 받은 dc 배열을 사용하여 가중치를 계산합니다. 이때, 가중치 계산은 다음과 같은 방법으로 이루어집니다.
- da[i] += dc[i] * s[i]: da 배열의 i번째 값에 dc[i] * s[i] 값을 더합니다.
- db[i] += dc[i] * (1-s[i]): db 배열의 i번째 값에 dc[i] * (1-s[i]) 값을 더합니다.
- ds[i] += dc[i] * (a[i] - b[i]): ds 배열의 i번째 값에 dc[i] * (a[i] - b[i]) 값을 더합니다.
이러한 가중치 계산을 수행하여 결과 값을 da, db, ds 배열에 저장합니다.
shortcut_cpu
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
void shortcut_cpu(int batch, int w1, int h1, int c1, float *add, int w2, int h2, int c2, float s1, float s2, float *out)
{
int stride = w1/w2;
int sample = w2/w1;
assert(stride == h1/h2);
assert(sample == h2/h1);
if(stride < 1) stride = 1;
if(sample < 1) sample = 1;
int minw = (w1 < w2) ? w1 : w2;
int minh = (h1 < h2) ? h1 : h2;
int minc = (c1 < c2) ? c1 : c2;
int i,j,k,b;
for(b = 0; b < batch; ++b){
for(k = 0; k < minc; ++k){
for(j = 0; j < minh; ++j){
for(i = 0; i < minw; ++i){
int out_index = i*sample + w2*(j*sample + h2*(k + c2*b));
int add_index = i*stride + w1*(j*stride + h1*(k + c1*b));
out[out_index] = s1*out[out_index] + s2*add[add_index];
}
}
}
}
}
함수 이름: shortcut_cpu
입력:
- batch: 미니배치 크기
- w1: 이전 레이어의 가로 크기
- h1: 이전 레이어의 세로 크기
- c1: 이전 레이어의 채널 수
- add: 이전 레이어의 출력값
- w2: 현재 레이어의 가로 크기
- h2: 현재 레이어의 세로 크기
- c2: 현재 레이어의 채널 수
- s1: 이전 레이어 출력값의 스케일
- s2: 현재 레이어 출력값의 스케일
동작: shortcut connection(잔여 연결)을 위한 함수로, 이전 레이어의 출력값(add)과 현재 레이어의 출력값(out)을 합하여 최종 출력값을 계산한다.
설명:
- stride와 sample 변수는 서로 반대 개념이며, 가로와 세로 방향의 크기 비율을 나타낸다. 이 값들을 이용해 출력값의 크기를 조정한다.
- 두 레이어의 크기 중에서 더 작은 값(minw, minh, minc)을 이용해 출력값의 인덱스를 계산한다.
- 각 미니배치(b), 채널(k), 세로 방향(j), 가로 방향(i)에 대해 반복문을 수행하며, out_index와 add_index를 계산하여 두 값을 가중합하여 최종 출력값을 계산한다.
- 이전 레이어 출력값(add)과 현재 레이어 출력값(out)은 서로 다른 크기를 가질 수 있으므로, stride와 sample 값에 따라 크기를 조정하여 합산한다.
- 출력값(out)에 이전 레이어 출력값의 스케일(s1)과 현재 레이어 출력값의 스케일(s2)을 곱하여 최종 출력값을 계산한다.
softmax_x_ent_cpu
1
2
3
4
5
6
7
8
9
10
void softmax_x_ent_cpu(int n, float *pred, float *truth, float *delta, float *error)
{
int i;
for(i = 0; i < n; ++i){
float t = truth[i];
float p = pred[i];
error[i] = (t) ? -log(p) : 0;
delta[i] = t-p;
}
}
함수 이름: softmax_x_ent_cpu
입력:
- n: 예측값(pred), 실제값(truth), 델타값(delta), 오차값(error)의 개수
- pred: 예측값 배열 포인터
- truth: 실제값 배열 포인터
- delta: 델타값 배열 포인터
- error: 오차값 배열 포인터
동작: softmax와 cross-entropy loss를 계산하는 함수이다.
설명:
- 함수는 예측값(pred), 실제값(truth)을 입력받아서, 델타값(delta)와 오차값(error)을 계산한다.
- softmax 함수는 다중 클래스 분류 문제에서 각 클래스에 대한 예측 확률을 구하기 위해 사용된다. 여기서는 softmax 함수의 결과값이 이미 주어진 것으로 가정한다.
- cross-entropy loss는 예측값과 실제값의 차이를 계산하는 손실 함수 중 하나이다. 예측값과 실제값의 차이를 log 함수를 이용해 계산하며, 실제값이 0인 경우에는 오차를 0으로 계산한다.
- 델타값은 신경망 역전파(backpropagation)에서 사용된다. 역전파에서는 출력층에서의 오차값을 입력층으로 전파시키며, 델타값은 이 과정에서 사용된다.
- 함수는 CPU에서 동작한다.